Auto merge of #74330 - Manishearth:rollup-mrc09pb, r=Manishearth
Rollup of 15 pull requests Successful merges: - #71237 (Add Ayu theme to rustdoc) - #73720 (Clean up E0704 error explanation) - #73866 (Obviate #[allow(improper_ctypes_definitions)]) - #73965 (typeck: check for infer before type impls trait) - #73986 (add (unchecked) indexing methods to raw (and NonNull) slices) - #74173 (Detect tuple struct incorrectly used as struct pat) - #74220 (Refactor Windows `parse_prefix`) - #74227 (Remove an unwrap in layout computation) - #74239 (Update llvm-project to latest origin/rustc/10.0-2020-05-05 commit ) - #74257 (don't mark linux kernel module targets as a unix environment) - #74270 (typeck: report placeholder type error w/out span) - #74296 (Clarify the description for rfind) - #74310 (Use `ArrayVec` in `SparseBitSet`.) - #74316 (Remove unnecessary type hints from Wake internals) - #74324 (Update Clippy) Failed merges: r? @ghost
This commit is contained in:
commit
2002ebacfb
186 changed files with 5659 additions and 1475 deletions
13
Cargo.lock
13
Cargo.lock
|
|
@ -94,6 +94,12 @@ dependencies = [
|
|||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
|
@ -164,7 +170,7 @@ version = "0.2.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.4.7",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
|
|
@ -714,7 +720,7 @@ version = "0.7.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.4.7",
|
||||
"cfg-if",
|
||||
"crossbeam-utils 0.6.5",
|
||||
"lazy_static",
|
||||
|
|
@ -3494,8 +3500,8 @@ dependencies = [
|
|||
name = "rustc_index"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.5.1",
|
||||
"rustc_serialize",
|
||||
"smallvec 1.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3996,6 +4002,7 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hir",
|
||||
"rustc_hir_pretty",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_middle",
|
||||
|
|
|
|||
|
|
@ -69,14 +69,13 @@ fn raw_waker<W: Wake + Send + Sync + 'static>(waker: Arc<W>) -> RawWaker {
|
|||
|
||||
// Wake by value, moving the Arc into the Wake::wake function
|
||||
unsafe fn wake<W: Wake + Send + Sync + 'static>(waker: *const ()) {
|
||||
let waker: Arc<W> = unsafe { Arc::from_raw(waker as *const W) };
|
||||
let waker = unsafe { Arc::from_raw(waker as *const W) };
|
||||
<W as Wake>::wake(waker);
|
||||
}
|
||||
|
||||
// Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it
|
||||
unsafe fn wake_by_ref<W: Wake + Send + Sync + 'static>(waker: *const ()) {
|
||||
let waker: ManuallyDrop<Arc<W>> =
|
||||
unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) };
|
||||
let waker = unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) };
|
||||
<W as Wake>::wake_by_ref(&waker);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@
|
|||
#![feature(associated_type_bounds)]
|
||||
#![feature(const_type_id)]
|
||||
#![feature(const_caller_location)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(no_niche)] // rust-lang/rust#68303
|
||||
#![feature(unsafe_block_in_unsafe_fn)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use super::*;
|
|||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics;
|
||||
use crate::mem;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
#[lang = "const_ptr"]
|
||||
impl<T: ?Sized> *const T {
|
||||
|
|
@ -826,6 +827,55 @@ impl<T> *const [T] {
|
|||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust: self }.raw }.len
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the slice's buffer.
|
||||
///
|
||||
/// This is equivalent to casting `self` to `*const T`, but more type-safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_get)]
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3);
|
||||
/// assert_eq!(slice.as_ptr(), 0 as *const i8);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
pub const fn as_ptr(self) -> *const T {
|
||||
self as *const T
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to an element or subslice, without doing bounds
|
||||
/// checking.
|
||||
///
|
||||
/// Calling this method with an out-of-bounds index or when `self` is not dereferencable
|
||||
/// is *[undefined behavior]* even if the resulting pointer is not used.
|
||||
///
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_ptr_get)]
|
||||
///
|
||||
/// let x = &[1, 2, 4] as *const [i32];
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(x.get_unchecked(1), x.as_ptr().add(1));
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked<I>(self, index: I) -> *const I::Output
|
||||
where
|
||||
I: SliceIndex<[T]>,
|
||||
{
|
||||
// SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
|
||||
unsafe { index.get_unchecked(self) }
|
||||
}
|
||||
}
|
||||
|
||||
// Equality for pointers
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::*;
|
||||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
#[lang = "mut_ptr"]
|
||||
impl<T: ?Sized> *mut T {
|
||||
|
|
@ -1014,7 +1015,6 @@ impl<T> *mut [T] {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_len)]
|
||||
///
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3);
|
||||
|
|
@ -1028,6 +1028,55 @@ impl<T> *mut [T] {
|
|||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust_mut: self }.raw }.len
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the slice's buffer.
|
||||
///
|
||||
/// This is equivalent to casting `self` to `*mut T`, but more type-safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_get)]
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3);
|
||||
/// assert_eq!(slice.as_mut_ptr(), 0 as *mut i8);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
pub const fn as_mut_ptr(self) -> *mut T {
|
||||
self as *mut T
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to an element or subslice, without doing bounds
|
||||
/// checking.
|
||||
///
|
||||
/// Calling this method with an out-of-bounds index or when `self` is not dereferencable
|
||||
/// is *[undefined behavior]* even if the resulting pointer is not used.
|
||||
///
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_ptr_get)]
|
||||
///
|
||||
/// let x = &mut [1, 2, 4] as *mut [i32];
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(x.get_unchecked_mut(1), x.as_mut_ptr().add(1));
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut<I>(self, index: I) -> *mut I::Output
|
||||
where
|
||||
I: SliceIndex<[T]>,
|
||||
{
|
||||
// SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
|
||||
unsafe { index.get_unchecked_mut(self) }
|
||||
}
|
||||
}
|
||||
|
||||
// Equality for pointers
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::marker::Unsize;
|
|||
use crate::mem;
|
||||
use crate::ops::{CoerceUnsized, DispatchFromDyn};
|
||||
use crate::ptr::Unique;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
/// `*mut T` but non-zero and covariant.
|
||||
///
|
||||
|
|
@ -192,7 +193,6 @@ impl<T> NonNull<[T]> {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)]
|
||||
///
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
|
||||
|
|
@ -204,6 +204,57 @@ impl<T> NonNull<[T]> {
|
|||
pub const fn len(self) -> usize {
|
||||
self.as_ptr().len()
|
||||
}
|
||||
|
||||
/// Returns a non-null pointer to the slice's buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
|
||||
/// assert_eq!(slice.as_non_null_ptr(), NonNull::new(1 as *mut i8).unwrap());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
pub const fn as_non_null_ptr(self) -> NonNull<T> {
|
||||
// SAFETY: We know `self` is non-null.
|
||||
unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) }
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to an element or subslice, without doing bounds
|
||||
/// checking.
|
||||
///
|
||||
/// Calling this method with an out-of-bounds index or when `self` is not dereferencable
|
||||
/// is *[undefined behavior]* even if the resulting pointer is not used.
|
||||
///
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let x = &mut [1, 2, 4];
|
||||
/// let x = NonNull::slice_from_raw_parts(NonNull::new(x.as_mut_ptr()).unwrap(), x.len());
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(x.get_unchecked_mut(1).as_ptr(), x.as_non_null_ptr().as_ptr().add(1));
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "slice_ptr_get", issue = "74265")]
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut<I>(self, index: I) -> NonNull<I::Output>
|
||||
where
|
||||
I: SliceIndex<[T]>,
|
||||
{
|
||||
// SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
|
||||
// As a consequence, the resulting pointer cannot be NULL.
|
||||
unsafe { NonNull::new_unchecked(self.as_ptr().get_unchecked_mut(index)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "nonnull", since = "1.25.0")]
|
||||
|
|
|
|||
|
|
@ -310,8 +310,10 @@ impl<T> [T] {
|
|||
where
|
||||
I: SliceIndex<Self>,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety requirements for `get_unchecked`.
|
||||
unsafe { index.get_unchecked(self) }
|
||||
// SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &*index.get_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to an element or subslice, without doing
|
||||
|
|
@ -342,8 +344,10 @@ impl<T> [T] {
|
|||
where
|
||||
I: SliceIndex<Self>,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`.
|
||||
unsafe { index.get_unchecked_mut(self) }
|
||||
// SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &mut *index.get_unchecked_mut(self) }
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the slice's buffer.
|
||||
|
|
@ -3010,6 +3014,9 @@ mod private_slice_index {
|
|||
}
|
||||
|
||||
/// A helper trait used for indexing operations.
|
||||
///
|
||||
/// Implementations of this trait have to promise that if the argument
|
||||
/// to `get_(mut_)unchecked` is a safe reference, then so is the result.
|
||||
#[stable(feature = "slice_get_slice", since = "1.28.0")]
|
||||
#[rustc_on_unimplemented(
|
||||
on(T = "str", label = "string indices are ranges of `usize`",),
|
||||
|
|
@ -3021,7 +3028,7 @@ see chapter in The Book <https://doc.rust-lang.org/book/ch08-02-strings.html#ind
|
|||
message = "the type `{T}` cannot be indexed by `{Self}`",
|
||||
label = "slice indices are of type `usize` or ranges of `usize`"
|
||||
)]
|
||||
pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
|
||||
pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
|
||||
/// The output type returned by methods.
|
||||
#[stable(feature = "slice_get_slice", since = "1.28.0")]
|
||||
type Output: ?Sized;
|
||||
|
|
@ -3038,21 +3045,21 @@ pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
|
|||
|
||||
/// Returns a shared reference to the output at this location, without
|
||||
/// performing any bounds checking.
|
||||
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
|
||||
/// even if the resulting reference is not used.
|
||||
/// Calling this method with an out-of-bounds index or a dangling `slice` pointer
|
||||
/// is *[undefined behavior]* even if the resulting reference is not used.
|
||||
///
|
||||
/// [undefined behavior]: ../../reference/behavior-considered-undefined.html
|
||||
#[unstable(feature = "slice_index_methods", issue = "none")]
|
||||
unsafe fn get_unchecked(self, slice: &T) -> &Self::Output;
|
||||
unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
|
||||
|
||||
/// Returns a mutable reference to the output at this location, without
|
||||
/// performing any bounds checking.
|
||||
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
|
||||
/// even if the resulting reference is not used.
|
||||
/// Calling this method with an out-of-bounds index or a dangling `slice` pointer
|
||||
/// is *[undefined behavior]* even if the resulting reference is not used.
|
||||
///
|
||||
/// [undefined behavior]: ../../reference/behavior-considered-undefined.html
|
||||
#[unstable(feature = "slice_index_methods", issue = "none")]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut T) -> &mut Self::Output;
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output;
|
||||
|
||||
/// Returns a shared reference to the output at this location, panicking
|
||||
/// if out of bounds.
|
||||
|
|
@ -3068,33 +3075,32 @@ pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
|
|||
}
|
||||
|
||||
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
|
||||
impl<T> SliceIndex<[T]> for usize {
|
||||
unsafe impl<T> SliceIndex<[T]> for usize {
|
||||
type Output = T;
|
||||
|
||||
#[inline]
|
||||
fn get(self, slice: &[T]) -> Option<&T> {
|
||||
if self < slice.len() { unsafe { Some(self.get_unchecked(slice)) } } else { None }
|
||||
if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
|
||||
if self < slice.len() { unsafe { Some(self.get_unchecked_mut(slice)) } } else { None }
|
||||
if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &T {
|
||||
// SAFETY: `slice` cannot be longer than `isize::MAX` and
|
||||
// the caller guarantees that `self` is in bounds of `slice`
|
||||
// so `self` cannot overflow an `isize`, so the call to `add` is safe.
|
||||
// The obtained pointer comes from a reference which is guaranteed
|
||||
// to be valid.
|
||||
unsafe { &*slice.as_ptr().add(self) }
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
|
||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||
// so the call to `add` is safe.
|
||||
unsafe { slice.as_ptr().add(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut T {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
|
||||
// SAFETY: see comments for `get_unchecked` above.
|
||||
unsafe { &mut *slice.as_mut_ptr().add(self) }
|
||||
unsafe { slice.as_mut_ptr().add(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3111,7 +3117,7 @@ impl<T> SliceIndex<[T]> for usize {
|
|||
}
|
||||
|
||||
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3119,7 +3125,7 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
if self.start > self.end || self.end > slice.len() {
|
||||
None
|
||||
} else {
|
||||
unsafe { Some(self.get_unchecked(slice)) }
|
||||
unsafe { Some(&*self.get_unchecked(slice)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3128,24 +3134,25 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
if self.start > self.end || self.end > slice.len() {
|
||||
None
|
||||
} else {
|
||||
unsafe { Some(self.get_unchecked_mut(slice)) }
|
||||
unsafe { Some(&mut *self.get_unchecked_mut(slice)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
// SAFETY: `slice` cannot be longer than `isize::MAX` and
|
||||
// the caller guarantees that `self` is in bounds of `slice`
|
||||
// so `self` cannot overflow an `isize`, so the call to `add` is safe.
|
||||
// Also, since the caller guarantees that `self` is in bounds of `slice`,
|
||||
// `from_raw_parts` will give a subslice of `slice` which is always safe.
|
||||
unsafe { from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) }
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||
// so the call to `add` is safe.
|
||||
unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
// SAFETY: see comments for `get_unchecked` above.
|
||||
unsafe { from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) }
|
||||
unsafe {
|
||||
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3155,7 +3162,7 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
} else if self.end > slice.len() {
|
||||
slice_index_len_fail(self.end, slice.len());
|
||||
}
|
||||
unsafe { self.get_unchecked(slice) }
|
||||
unsafe { &*self.get_unchecked(slice) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3165,12 +3172,12 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
} else if self.end > slice.len() {
|
||||
slice_index_len_fail(self.end, slice.len());
|
||||
}
|
||||
unsafe { self.get_unchecked_mut(slice) }
|
||||
unsafe { &mut *self.get_unchecked_mut(slice) }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3184,13 +3191,13 @@ impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (0..self.end).get_unchecked(slice) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (0..self.end).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
@ -3207,7 +3214,7 @@ impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
|
|||
}
|
||||
|
||||
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3221,13 +3228,13 @@ impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (self.start..slice.len()).get_unchecked(slice) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (self.start..slice.len()).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
@ -3244,7 +3251,7 @@ impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
|
|||
}
|
||||
|
||||
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::RangeFull {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::RangeFull {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3258,12 +3265,12 @@ impl<T> SliceIndex<[T]> for ops::RangeFull {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
slice
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
slice
|
||||
}
|
||||
|
||||
|
|
@ -3279,7 +3286,7 @@ impl<T> SliceIndex<[T]> for ops::RangeFull {
|
|||
}
|
||||
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3297,13 +3304,13 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
@ -3326,7 +3333,7 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
|
|||
}
|
||||
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
|
||||
unsafe impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
|
||||
type Output = [T];
|
||||
|
||||
#[inline]
|
||||
|
|
@ -3340,13 +3347,13 @@ impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (0..=self.end).get_unchecked(slice) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
// SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (0..=self.end).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1731,7 +1731,8 @@ Section: Trait implementations
|
|||
mod traits {
|
||||
use crate::cmp::Ordering;
|
||||
use crate::ops;
|
||||
use crate::slice::{self, SliceIndex};
|
||||
use crate::ptr;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
/// Implements ordering of strings.
|
||||
///
|
||||
|
|
@ -1822,7 +1823,7 @@ mod traits {
|
|||
///
|
||||
/// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`.
|
||||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
impl SliceIndex<str> for ops::RangeFull {
|
||||
unsafe impl SliceIndex<str> for ops::RangeFull {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
|
|
@ -1833,11 +1834,11 @@ mod traits {
|
|||
Some(slice)
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
slice
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
slice
|
||||
}
|
||||
#[inline]
|
||||
|
|
@ -1886,7 +1887,7 @@ mod traits {
|
|||
/// // &s[3 .. 100];
|
||||
/// ```
|
||||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
impl SliceIndex<str> for ops::Range<usize> {
|
||||
unsafe impl SliceIndex<str> for ops::Range<usize> {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
|
|
@ -1894,8 +1895,10 @@ mod traits {
|
|||
&& slice.is_char_boundary(self.start)
|
||||
&& slice.is_char_boundary(self.end)
|
||||
{
|
||||
// SAFETY: just checked that `start` and `end` are on a char boundary.
|
||||
Some(unsafe { self.get_unchecked(slice) })
|
||||
// SAFETY: just checked that `start` and `end` are on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
// We also checked char boundaries, so this is valid UTF-8.
|
||||
Some(unsafe { &*self.get_unchecked(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -1907,34 +1910,28 @@ mod traits {
|
|||
&& slice.is_char_boundary(self.end)
|
||||
{
|
||||
// SAFETY: just checked that `start` and `end` are on a char boundary.
|
||||
Some(unsafe { self.get_unchecked_mut(slice) })
|
||||
// We know the pointer is unique because we got it from `slice`.
|
||||
Some(unsafe { &mut *self.get_unchecked_mut(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
let slice = slice as *const [u8];
|
||||
// SAFETY: the caller guarantees that `self` is in bounds of `slice`
|
||||
// which satisfies all the conditions for `add`.
|
||||
let ptr = unsafe { slice.as_ptr().add(self.start) };
|
||||
let len = self.end - self.start;
|
||||
// SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
|
||||
// we can safely construct a subslice with `from_raw_parts` and use it
|
||||
// since we return a shared thus immutable reference.
|
||||
// The call to `from_utf8_unchecked` is safe since the data comes from
|
||||
// a `str` which is guaranteed to be valid utf8, since the caller
|
||||
// must guarantee that `self.start` and `self.end` are char boundaries.
|
||||
unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
|
||||
ptr::slice_from_raw_parts(ptr, len) as *const str
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
let slice = slice as *mut [u8];
|
||||
// SAFETY: see comments for `get_unchecked`.
|
||||
let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
|
||||
let len = self.end - self.start;
|
||||
// SAFETY: mostly identical to the comments for `get_unchecked`, except that we
|
||||
// can return a mutable reference since the caller passed a mutable reference
|
||||
// and is thus guaranteed to have exclusive write access to `slice`.
|
||||
unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, len)) }
|
||||
ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
|
||||
}
|
||||
#[inline]
|
||||
fn index(self, slice: &str) -> &Self::Output {
|
||||
|
|
@ -1949,8 +1946,9 @@ mod traits {
|
|||
&& slice.is_char_boundary(self.start)
|
||||
&& slice.is_char_boundary(self.end)
|
||||
{
|
||||
// SAFETY: just checked that `start` and `end` are on a char boundary.
|
||||
unsafe { self.get_unchecked_mut(slice) }
|
||||
// SAFETY: just checked that `start` and `end` are on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
unsafe { &mut *self.get_unchecked_mut(slice) }
|
||||
} else {
|
||||
super::slice_error_fail(slice, self.start, self.end)
|
||||
}
|
||||
|
|
@ -1973,13 +1971,14 @@ mod traits {
|
|||
/// Panics if `end` does not point to the starting byte offset of a
|
||||
/// character (as defined by `is_char_boundary`), or if `end > len`.
|
||||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
impl SliceIndex<str> for ops::RangeTo<usize> {
|
||||
unsafe impl SliceIndex<str> for ops::RangeTo<usize> {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
if slice.is_char_boundary(self.end) {
|
||||
// SAFETY: just checked that `end` is on a char boundary.
|
||||
Some(unsafe { self.get_unchecked(slice) })
|
||||
// SAFETY: just checked that `end` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
Some(unsafe { &*self.get_unchecked(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -1987,30 +1986,24 @@ mod traits {
|
|||
#[inline]
|
||||
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
|
||||
if slice.is_char_boundary(self.end) {
|
||||
// SAFETY: just checked that `end` is on a char boundary.
|
||||
Some(unsafe { self.get_unchecked_mut(slice) })
|
||||
// SAFETY: just checked that `end` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
Some(unsafe { &mut *self.get_unchecked_mut(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
let slice = slice as *const [u8];
|
||||
let ptr = slice.as_ptr();
|
||||
// SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
|
||||
// we can safely construct a subslice with `from_raw_parts` and use it
|
||||
// since we return a shared thus immutable reference.
|
||||
// The call to `from_utf8_unchecked` is safe since the data comes from
|
||||
// a `str` which is guaranteed to be valid utf8, since the caller
|
||||
// must guarantee that `self.end` is a char boundary.
|
||||
unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, self.end)) }
|
||||
ptr::slice_from_raw_parts(ptr, self.end) as *const str
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
let slice = slice as *mut [u8];
|
||||
let ptr = slice.as_mut_ptr();
|
||||
// SAFETY: mostly identical to `get_unchecked`, except that we can safely
|
||||
// return a mutable reference since the caller passed a mutable reference
|
||||
// and is thus guaranteed to have exclusive write access to `slice`.
|
||||
unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, self.end)) }
|
||||
ptr::slice_from_raw_parts_mut(ptr, self.end) as *mut str
|
||||
}
|
||||
#[inline]
|
||||
fn index(self, slice: &str) -> &Self::Output {
|
||||
|
|
@ -2020,8 +2013,9 @@ mod traits {
|
|||
#[inline]
|
||||
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
if slice.is_char_boundary(self.end) {
|
||||
// SAFETY: just checked that `end` is on a char boundary.
|
||||
unsafe { self.get_unchecked_mut(slice) }
|
||||
// SAFETY: just checked that `end` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
unsafe { &mut *self.get_unchecked_mut(slice) }
|
||||
} else {
|
||||
super::slice_error_fail(slice, 0, self.end)
|
||||
}
|
||||
|
|
@ -2045,13 +2039,14 @@ mod traits {
|
|||
/// Panics if `begin` does not point to the starting byte offset of
|
||||
/// a character (as defined by `is_char_boundary`), or if `begin >= len`.
|
||||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
impl SliceIndex<str> for ops::RangeFrom<usize> {
|
||||
unsafe impl SliceIndex<str> for ops::RangeFrom<usize> {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
if slice.is_char_boundary(self.start) {
|
||||
// SAFETY: just checked that `start` is on a char boundary.
|
||||
Some(unsafe { self.get_unchecked(slice) })
|
||||
// SAFETY: just checked that `start` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
Some(unsafe { &*self.get_unchecked(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -2059,35 +2054,29 @@ mod traits {
|
|||
#[inline]
|
||||
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
|
||||
if slice.is_char_boundary(self.start) {
|
||||
// SAFETY: just checked that `start` is on a char boundary.
|
||||
Some(unsafe { self.get_unchecked_mut(slice) })
|
||||
// SAFETY: just checked that `start` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
Some(unsafe { &mut *self.get_unchecked_mut(slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
let slice = slice as *const [u8];
|
||||
// SAFETY: the caller guarantees that `self` is in bounds of `slice`
|
||||
// which satisfies all the conditions for `add`.
|
||||
let ptr = unsafe { slice.as_ptr().add(self.start) };
|
||||
let len = slice.len() - self.start;
|
||||
// SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
|
||||
// we can safely construct a subslice with `from_raw_parts` and use it
|
||||
// since we return a shared thus immutable reference.
|
||||
// The call to `from_utf8_unchecked` is safe since the data comes from
|
||||
// a `str` which is guaranteed to be valid utf8, since the caller
|
||||
// must guarantee that `self.start` is a char boundary.
|
||||
unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
|
||||
ptr::slice_from_raw_parts(ptr, len) as *const str
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
let slice = slice as *mut [u8];
|
||||
// SAFETY: identical to `get_unchecked`.
|
||||
let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
|
||||
let len = slice.len() - self.start;
|
||||
// SAFETY: mostly identical to `get_unchecked`, except that we can safely
|
||||
// return a mutable reference since the caller passed a mutable reference
|
||||
// and is thus guaranteed to have exclusive write access to `slice`.
|
||||
unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, len)) }
|
||||
ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
|
||||
}
|
||||
#[inline]
|
||||
fn index(self, slice: &str) -> &Self::Output {
|
||||
|
|
@ -2097,8 +2086,9 @@ mod traits {
|
|||
#[inline]
|
||||
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
if slice.is_char_boundary(self.start) {
|
||||
// SAFETY: just checked that `start` is on a char boundary.
|
||||
unsafe { self.get_unchecked_mut(slice) }
|
||||
// SAFETY: just checked that `start` is on a char boundary,
|
||||
// and we are passing in a safe reference, so the return value will also be one.
|
||||
unsafe { &mut *self.get_unchecked_mut(slice) }
|
||||
} else {
|
||||
super::slice_error_fail(slice, self.start, slice.len())
|
||||
}
|
||||
|
|
@ -2122,7 +2112,7 @@ mod traits {
|
|||
/// to the ending byte offset of a character (`end + 1` is either a starting
|
||||
/// byte offset or equal to `len`), if `begin > end`, or if `end >= len`.
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl SliceIndex<str> for ops::RangeInclusive<usize> {
|
||||
unsafe impl SliceIndex<str> for ops::RangeInclusive<usize> {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
|
|
@ -2141,12 +2131,12 @@ mod traits {
|
|||
}
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) }
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
@ -2181,7 +2171,7 @@ mod traits {
|
|||
/// (`end + 1` is either a starting byte offset as defined by
|
||||
/// `is_char_boundary`, or equal to `len`), or if `end >= len`.
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl SliceIndex<str> for ops::RangeToInclusive<usize> {
|
||||
unsafe impl SliceIndex<str> for ops::RangeToInclusive<usize> {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn get(self, slice: &str) -> Option<&Self::Output> {
|
||||
|
|
@ -2192,12 +2182,12 @@ mod traits {
|
|||
if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) }
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (..self.end + 1).get_unchecked(slice) }
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (..self.end + 1).get_unchecked_mut(slice) }
|
||||
}
|
||||
|
|
@ -2560,8 +2550,10 @@ impl str {
|
|||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
|
||||
unsafe { i.get_unchecked(self) }
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &*i.get_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Returns a mutable, unchecked subslice of `str`.
|
||||
|
|
@ -2593,8 +2585,10 @@ impl str {
|
|||
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { i.get_unchecked_mut(self) }
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &mut *i.get_unchecked_mut(self) }
|
||||
}
|
||||
|
||||
/// Creates a string slice from another string slice, bypassing safety
|
||||
|
|
@ -2644,8 +2638,10 @@ impl str {
|
|||
#[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked(begin..end)` instead")]
|
||||
#[inline]
|
||||
pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
|
||||
unsafe { (begin..end).get_unchecked(self) }
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &*(begin..end).get_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Creates a string slice from another string slice, bypassing safety
|
||||
|
|
@ -2676,8 +2672,10 @@ impl str {
|
|||
#[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked_mut(begin..end)` instead")]
|
||||
#[inline]
|
||||
pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
|
||||
unsafe { (begin..end).get_unchecked_mut(self) }
|
||||
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`;
|
||||
// the slice is dereferencable because `self` is a safe reference.
|
||||
// The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
|
||||
unsafe { &mut *(begin..end).get_unchecked_mut(self) }
|
||||
}
|
||||
|
||||
/// Divide one string slice into two at an index.
|
||||
|
|
@ -3158,11 +3156,11 @@ impl str {
|
|||
/// Simple patterns:
|
||||
///
|
||||
/// ```
|
||||
/// let s = "Löwe 老虎 Léopard";
|
||||
/// let s = "Löwe 老虎 Léopard Gepardi";
|
||||
///
|
||||
/// assert_eq!(s.find('L'), Some(0));
|
||||
/// assert_eq!(s.find('é'), Some(14));
|
||||
/// assert_eq!(s.find("Léopard"), Some(13));
|
||||
/// assert_eq!(s.find("pard"), Some(17));
|
||||
/// ```
|
||||
///
|
||||
/// More complex patterns using point-free style and closures:
|
||||
|
|
@ -3190,8 +3188,8 @@ impl str {
|
|||
pat.into_searcher(self).next_match().map(|(i, _)| i)
|
||||
}
|
||||
|
||||
/// Returns the byte index of the last character of this string slice that
|
||||
/// matches the pattern.
|
||||
/// Returns the byte index for the first character of the rightmost match of the pattern in
|
||||
/// this string slice.
|
||||
///
|
||||
/// Returns [`None`] if the pattern doesn't match.
|
||||
///
|
||||
|
|
@ -3207,10 +3205,11 @@ impl str {
|
|||
/// Simple patterns:
|
||||
///
|
||||
/// ```
|
||||
/// let s = "Löwe 老虎 Léopard";
|
||||
/// let s = "Löwe 老虎 Léopard Gepardi";
|
||||
///
|
||||
/// assert_eq!(s.rfind('L'), Some(13));
|
||||
/// assert_eq!(s.rfind('é'), Some(14));
|
||||
/// assert_eq!(s.rfind("pard"), Some(24));
|
||||
/// ```
|
||||
///
|
||||
/// More complex patterns with closures:
|
||||
|
|
|
|||
|
|
@ -451,6 +451,7 @@ E0765: include_str!("./error_codes/E0765.md"),
|
|||
E0766: include_str!("./error_codes/E0766.md"),
|
||||
E0767: include_str!("./error_codes/E0767.md"),
|
||||
E0768: include_str!("./error_codes/E0768.md"),
|
||||
E0769: include_str!("./error_codes/E0769.md"),
|
||||
;
|
||||
// E0006, // merged with E0005
|
||||
// E0008, // cannot bind by-move into a pattern guard
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
This error indicates that a incorrect visibility restriction was specified.
|
||||
An incorrect visibility restriction was specified.
|
||||
|
||||
Example of erroneous code:
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0704
|
||||
mod foo {
|
||||
|
|
@ -12,6 +12,7 @@ mod foo {
|
|||
|
||||
To make struct `Bar` only visible in module `foo` the `in` keyword should be
|
||||
used:
|
||||
|
||||
```
|
||||
mod foo {
|
||||
pub(in crate::foo) struct Bar {
|
||||
|
|
|
|||
39
src/librustc_error_codes/error_codes/E0769.md
Normal file
39
src/librustc_error_codes/error_codes/E0769.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
A tuple struct or tuple variant was used in a pattern as if it were a
|
||||
struct or struct variant.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0769
|
||||
enum E {
|
||||
A(i32),
|
||||
}
|
||||
let e = E::A(42);
|
||||
match e {
|
||||
E::A { number } => println!("{}", x),
|
||||
}
|
||||
```
|
||||
|
||||
To fix this error, you can use the tuple pattern:
|
||||
|
||||
```
|
||||
# enum E {
|
||||
# A(i32),
|
||||
# }
|
||||
# let e = E::A(42);
|
||||
match e {
|
||||
E::A(number) => println!("{}", number),
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can also use the struct pattern by using the correct
|
||||
field names and binding them to new identifiers:
|
||||
|
||||
```
|
||||
# enum E {
|
||||
# A(i32),
|
||||
# }
|
||||
# let e = E::A(42);
|
||||
match e {
|
||||
E::A { 0: number } => println!("{}", number),
|
||||
}
|
||||
```
|
||||
|
|
@ -11,4 +11,4 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
rustc_serialize = { path = "../librustc_serialize" }
|
||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||
arrayvec = "0.5.1"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::vec::{Idx, IndexVec};
|
||||
use smallvec::SmallVec;
|
||||
use arrayvec::ArrayVec;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::marker::PhantomData;
|
||||
|
|
@ -355,20 +355,19 @@ where
|
|||
const SPARSE_MAX: usize = 8;
|
||||
|
||||
/// A fixed-size bitset type with a sparse representation and a maximum of
|
||||
/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with
|
||||
/// no duplicates; although `SmallVec` can spill its elements to the heap, that
|
||||
/// never happens within this type because of the `SPARSE_MAX` limit.
|
||||
/// `SPARSE_MAX` elements. The elements are stored as a sorted `ArrayVec` with
|
||||
/// no duplicates.
|
||||
///
|
||||
/// This type is used by `HybridBitSet`; do not use directly.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SparseBitSet<T: Idx> {
|
||||
domain_size: usize,
|
||||
elems: SmallVec<[T; SPARSE_MAX]>,
|
||||
elems: ArrayVec<[T; SPARSE_MAX]>,
|
||||
}
|
||||
|
||||
impl<T: Idx> SparseBitSet<T> {
|
||||
fn new_empty(domain_size: usize) -> Self {
|
||||
SparseBitSet { domain_size, elems: SmallVec::new() }
|
||||
SparseBitSet { domain_size, elems: ArrayVec::new() }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
|
|
|
|||
|
|
@ -774,12 +774,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
|||
(present_variants.next(), present_variants.next())
|
||||
};
|
||||
let present_first = match present_first {
|
||||
present_first @ Some(_) => present_first,
|
||||
Some(present_first) => present_first,
|
||||
// Uninhabited because it has no variants, or only absent ones.
|
||||
None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)),
|
||||
// If it's a struct, still compute a layout so that we can still compute the
|
||||
// field offsets.
|
||||
None => Some(VariantIdx::new(0)),
|
||||
None => VariantIdx::new(0),
|
||||
};
|
||||
|
||||
let is_struct = !def.is_enum() ||
|
||||
|
|
@ -791,7 +791,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
|||
// Struct, or univariant enum equivalent to a struct.
|
||||
// (Typechecking will reject discriminant-sizing attrs.)
|
||||
|
||||
let v = present_first.unwrap();
|
||||
let v = present_first;
|
||||
let kind = if def.is_enum() || variants[v].is_empty() {
|
||||
StructKind::AlwaysSized
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ pub fn opts() -> TargetOptions {
|
|||
needs_plt: true,
|
||||
relro_level: RelroLevel::Full,
|
||||
relocation_model: RelocModel::Static,
|
||||
target_family: Some("unix".to_string()),
|
||||
pre_link_args,
|
||||
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ rustc_attr = { path = "../librustc_attr" }
|
|||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
rustc_errors = { path = "../librustc_errors" }
|
||||
rustc_hir = { path = "../librustc_hir" }
|
||||
rustc_hir_pretty = { path = "../librustc_hir_pretty" }
|
||||
rustc_target = { path = "../librustc_target" }
|
||||
rustc_session = { path = "../librustc_session" }
|
||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||
|
|
|
|||
|
|
@ -3049,14 +3049,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let bare_fn_ty =
|
||||
ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi));
|
||||
|
||||
if let (false, Some(ident_span)) = (self.allow_ty_infer(), ident_span) {
|
||||
if !self.allow_ty_infer() {
|
||||
// We always collect the spans for placeholder types when evaluating `fn`s, but we
|
||||
// only want to emit an error complaining about them if infer types (`_`) are not
|
||||
// allowed. `allow_ty_infer` gates this behavior. We check for the presence of
|
||||
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
|
||||
crate::collect::placeholder_type_error(
|
||||
tcx,
|
||||
ident_span.shrink_to_hi(),
|
||||
ident_span.map(|sp| sp.shrink_to_hi()),
|
||||
&generics.params[..],
|
||||
visitor.0,
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
// Check for infer types because cases like `Option<{integer}>` would
|
||||
// panic otherwise.
|
||||
if !expr_ty.has_infer_types()
|
||||
&& !ty.has_infer_types()
|
||||
&& fcx.tcx.type_implements_trait((
|
||||
from_trait,
|
||||
ty,
|
||||
|
|
|
|||
|
|
@ -1082,20 +1082,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.filter(|ident| !used_fields.contains_key(&ident))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !inexistent_fields.is_empty() && !variant.recovered {
|
||||
self.error_inexistent_fields(
|
||||
let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered {
|
||||
Some(self.error_inexistent_fields(
|
||||
adt.variant_descr(),
|
||||
&inexistent_fields,
|
||||
&mut unmentioned_fields,
|
||||
variant,
|
||||
);
|
||||
}
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Require `..` if struct has non_exhaustive attribute.
|
||||
if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
|
||||
self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
|
||||
}
|
||||
|
||||
let mut unmentioned_err = None;
|
||||
// Report an error if incorrect number of the fields were specified.
|
||||
if adt.is_union() {
|
||||
if fields.len() != 1 {
|
||||
|
|
@ -1107,7 +1110,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
|
||||
}
|
||||
} else if !etc && !unmentioned_fields.is_empty() {
|
||||
self.error_unmentioned_fields(pat.span, &unmentioned_fields, variant);
|
||||
unmentioned_err = Some(self.error_unmentioned_fields(pat.span, &unmentioned_fields));
|
||||
}
|
||||
match (inexistent_fields_err, unmentioned_err) {
|
||||
(Some(mut i), Some(mut u)) => {
|
||||
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
|
||||
// We don't want to show the inexistent fields error when this was
|
||||
// `Foo { a, b }` when it should have been `Foo(a, b)`.
|
||||
i.delay_as_bug();
|
||||
u.delay_as_bug();
|
||||
e.emit();
|
||||
} else {
|
||||
i.emit();
|
||||
u.emit();
|
||||
}
|
||||
}
|
||||
(None, Some(mut err)) | (Some(mut err), None) => {
|
||||
err.emit();
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
no_field_errors
|
||||
}
|
||||
|
|
@ -1154,7 +1175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
inexistent_fields: &[Ident],
|
||||
unmentioned_fields: &mut Vec<Ident>,
|
||||
variant: &ty::VariantDef,
|
||||
) {
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
|
||||
(format!("a field named `{}`", inexistent_fields[0]), "this", "")
|
||||
|
|
@ -1221,15 +1242,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
it explicitly.",
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
err
|
||||
}
|
||||
|
||||
fn error_tuple_variant_as_struct_pat(
|
||||
&self,
|
||||
pat: &Pat<'_>,
|
||||
fields: &'tcx [hir::FieldPat<'tcx>],
|
||||
variant: &ty::VariantDef,
|
||||
) -> Option<DiagnosticBuilder<'tcx>> {
|
||||
if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) {
|
||||
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_qpath(qpath, false)
|
||||
});
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
pat.span,
|
||||
E0769,
|
||||
"tuple variant `{}` written as struct variant",
|
||||
path
|
||||
);
|
||||
let (sugg, appl) = if fields.len() == variant.fields.len() {
|
||||
(
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
|
||||
Ok(f) => f,
|
||||
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_pat(f.pat)
|
||||
}),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
variant.fields.iter().map(|_| "_").collect::<Vec<&str>>().join(", "),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
};
|
||||
err.span_suggestion(
|
||||
pat.span,
|
||||
"use the tuple variant pattern syntax instead",
|
||||
format!("{}({})", path, sugg),
|
||||
appl,
|
||||
);
|
||||
return Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn error_unmentioned_fields(
|
||||
&self,
|
||||
span: Span,
|
||||
unmentioned_fields: &[Ident],
|
||||
variant: &ty::VariantDef,
|
||||
) {
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
let field_names = if unmentioned_fields.len() == 1 {
|
||||
format!("field `{}`", unmentioned_fields[0])
|
||||
} else {
|
||||
|
|
@ -1248,9 +1316,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
field_names
|
||||
);
|
||||
diag.span_label(span, format!("missing {}", field_names));
|
||||
if variant.ctor_kind == CtorKind::Fn {
|
||||
diag.note("trying to match a tuple variant with a struct variant pattern");
|
||||
}
|
||||
if self.tcx.sess.teach(&diag.get_code().unwrap()) {
|
||||
diag.note(
|
||||
"This error indicates that a pattern for a struct fails to specify a \
|
||||
|
|
@ -1259,7 +1324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ignore unwanted fields.",
|
||||
);
|
||||
}
|
||||
diag.emit();
|
||||
diag
|
||||
}
|
||||
|
||||
fn check_pat_box(
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ struct CollectItemTypesVisitor<'tcx> {
|
|||
/// all already existing generic type parameters to avoid suggesting a name that is already in use.
|
||||
crate fn placeholder_type_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
span: Span,
|
||||
span: Option<Span>,
|
||||
generics: &[hir::GenericParam<'_>],
|
||||
placeholder_types: Vec<Span>,
|
||||
suggest: bool,
|
||||
|
|
@ -137,12 +137,15 @@ crate fn placeholder_type_error(
|
|||
if placeholder_types.is_empty() {
|
||||
return;
|
||||
}
|
||||
let type_name = generics.next_type_param_name(None);
|
||||
|
||||
let type_name = generics.next_type_param_name(None);
|
||||
let mut sugg: Vec<_> =
|
||||
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
|
||||
|
||||
if generics.is_empty() {
|
||||
sugg.push((span, format!("<{}>", type_name)));
|
||||
if let Some(span) = span {
|
||||
sugg.push((span, format!("<{}>", type_name)));
|
||||
}
|
||||
} else if let Some(arg) = generics.iter().find(|arg| match arg.name {
|
||||
hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true,
|
||||
_ => false,
|
||||
|
|
@ -158,6 +161,7 @@ crate fn placeholder_type_error(
|
|||
format!(", {}", type_name),
|
||||
));
|
||||
}
|
||||
|
||||
let mut err = bad_placeholder_type(tcx, placeholder_types);
|
||||
if suggest {
|
||||
err.multipart_suggestion(
|
||||
|
|
@ -186,7 +190,7 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir
|
|||
let mut visitor = PlaceholderHirTyCollector::default();
|
||||
visitor.visit_item(item);
|
||||
|
||||
placeholder_type_error(tcx, generics.span, &generics.params[..], visitor.0, suggest);
|
||||
placeholder_type_error(tcx, Some(generics.span), &generics.params[..], visitor.0, suggest);
|
||||
}
|
||||
|
||||
impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
|
||||
|
|
@ -722,7 +726,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) {
|
|||
// Account for `const C: _;` and `type T = _;`.
|
||||
let mut visitor = PlaceholderHirTyCollector::default();
|
||||
visitor.visit_trait_item(trait_item);
|
||||
placeholder_type_error(tcx, DUMMY_SP, &[], visitor.0, false);
|
||||
placeholder_type_error(tcx, None, &[], visitor.0, false);
|
||||
}
|
||||
|
||||
hir::TraitItemKind::Type(_, None) => {}
|
||||
|
|
@ -745,7 +749,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) {
|
|||
// Account for `type T = _;`
|
||||
let mut visitor = PlaceholderHirTyCollector::default();
|
||||
visitor.visit_impl_item(impl_item);
|
||||
placeholder_type_error(tcx, DUMMY_SP, &[], visitor.0, false);
|
||||
placeholder_type_error(tcx, None, &[], visitor.0, false);
|
||||
}
|
||||
hir::ImplItemKind::Const(..) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use crate::core::new_handler;
|
|||
use crate::externalfiles::ExternalHtml;
|
||||
use crate::html;
|
||||
use crate::html::markdown::IdMap;
|
||||
use crate::html::render::StylePath;
|
||||
use crate::html::static_files;
|
||||
use crate::opts;
|
||||
use crate::passes::{self, Condition, DefaultPassOption};
|
||||
|
|
@ -207,7 +208,7 @@ pub struct RenderOptions {
|
|||
pub sort_modules_alphabetically: bool,
|
||||
/// List of themes to extend the docs with. Original argument name is included to assist in
|
||||
/// displaying errors if it fails a theme check.
|
||||
pub themes: Vec<PathBuf>,
|
||||
pub themes: Vec<StylePath>,
|
||||
/// If present, CSS file that contains rules to add to the default CSS.
|
||||
pub extension_css: Option<PathBuf>,
|
||||
/// A map of crate names to the URL to use instead of querying the crate's `html_root_url`.
|
||||
|
|
@ -410,7 +411,7 @@ impl Options {
|
|||
))
|
||||
.emit();
|
||||
}
|
||||
themes.push(theme_file);
|
||||
themes.push(StylePath { path: theme_file, disabled: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||
use crate::externalfiles::ExternalHtml;
|
||||
use crate::html::escape::Escape;
|
||||
use crate::html::format::{Buffer, Print};
|
||||
use crate::html::render::ensure_trailing_slash;
|
||||
use crate::html::render::{ensure_trailing_slash, StylePath};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Layout {
|
||||
|
|
@ -36,7 +36,7 @@ pub fn render<T: Print, S: Print>(
|
|||
page: &Page<'_>,
|
||||
sidebar: S,
|
||||
t: T,
|
||||
themes: &[PathBuf],
|
||||
style_files: &[StylePath],
|
||||
) -> String {
|
||||
let static_root_path = page.static_root_path.unwrap_or(page.root_path);
|
||||
format!(
|
||||
|
|
@ -52,10 +52,7 @@ pub fn render<T: Print, S: Print>(
|
|||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}normalize{suffix}.css\">\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}rustdoc{suffix}.css\" \
|
||||
id=\"mainThemeStyle\">\
|
||||
{themes}\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}dark{suffix}.css\">\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}light{suffix}.css\" \
|
||||
id=\"themeStyle\">\
|
||||
{style_files}\
|
||||
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
|
||||
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
|
||||
{css_extension}\
|
||||
|
|
@ -172,13 +169,19 @@ pub fn render<T: Print, S: Print>(
|
|||
after_content = layout.external_html.after_content,
|
||||
sidebar = Buffer::html().to_display(sidebar),
|
||||
krate = layout.krate,
|
||||
themes = themes
|
||||
style_files = style_files
|
||||
.iter()
|
||||
.filter_map(|t| t.file_stem())
|
||||
.filter_map(|t| t.to_str())
|
||||
.filter_map(|t| {
|
||||
if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
|
||||
})
|
||||
.filter_map(|t| {
|
||||
if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None }
|
||||
})
|
||||
.map(|t| format!(
|
||||
r#"<link rel="stylesheet" type="text/css" href="{}.css">"#,
|
||||
Escape(&format!("{}{}{}", static_root_path, t, page.resource_suffix))
|
||||
r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
|
||||
Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
|
||||
if t.1 { "disabled" } else { "" },
|
||||
if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
|
||||
))
|
||||
.collect::<String>(),
|
||||
suffix = page.resource_suffix,
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ crate struct SharedContext {
|
|||
/// This flag indicates whether listings of modules (in the side bar and documentation itself)
|
||||
/// should be ordered alphabetically or in order of appearance (in the source code).
|
||||
pub sort_modules_alphabetically: bool,
|
||||
/// Additional themes to be added to the generated docs.
|
||||
pub themes: Vec<PathBuf>,
|
||||
/// Additional CSS files to be added to the generated docs.
|
||||
pub style_files: Vec<StylePath>,
|
||||
/// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
|
||||
/// "light-v2.css").
|
||||
pub resource_suffix: String,
|
||||
|
|
@ -418,6 +418,14 @@ impl Serialize for TypeWithKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StylePath {
|
||||
/// The path to the theme
|
||||
pub path: PathBuf,
|
||||
/// What the `disabled` attribute should be set to in the HTML tag
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
|
||||
thread_local!(pub static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
|
||||
|
||||
|
|
@ -461,7 +469,7 @@ pub fn run(
|
|||
id_map,
|
||||
playground_url,
|
||||
sort_modules_alphabetically,
|
||||
themes,
|
||||
themes: style_files,
|
||||
extension_css,
|
||||
extern_html_root_urls,
|
||||
resource_suffix,
|
||||
|
|
@ -531,7 +539,7 @@ pub fn run(
|
|||
layout,
|
||||
created_dirs: Default::default(),
|
||||
sort_modules_alphabetically,
|
||||
themes,
|
||||
style_files,
|
||||
resource_suffix,
|
||||
static_root_path,
|
||||
fs: DocFS::new(&errors),
|
||||
|
|
@ -540,6 +548,19 @@ pub fn run(
|
|||
playground,
|
||||
};
|
||||
|
||||
// Add the default themes to the `Vec` of stylepaths
|
||||
//
|
||||
// Note that these must be added before `sources::render` is called
|
||||
// so that the resulting source pages are styled
|
||||
//
|
||||
// `light.css` is not disabled because it is the stylesheet that stays loaded
|
||||
// by the browser as the theme stylesheet. The theme system (hackily) works by
|
||||
// changing the href to this stylesheet. All other themes are disabled to
|
||||
// prevent rule conflicts
|
||||
scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
|
||||
scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
|
||||
scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
|
||||
|
||||
let dst = output;
|
||||
scx.ensure_dir(&dst)?;
|
||||
krate = sources::render(&dst, &mut scx, krate)?;
|
||||
|
|
@ -616,11 +637,40 @@ fn write_shared(
|
|||
// then we'll run over the "official" styles.
|
||||
let mut themes: FxHashSet<String> = FxHashSet::default();
|
||||
|
||||
for entry in &cx.shared.themes {
|
||||
let content = try_err!(fs::read(&entry), &entry);
|
||||
let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry);
|
||||
let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry);
|
||||
cx.shared.fs.write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
|
||||
for entry in &cx.shared.style_files {
|
||||
let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
|
||||
let extension =
|
||||
try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
|
||||
|
||||
// Handle the official themes
|
||||
match theme {
|
||||
"light" => write_minify(
|
||||
&cx.shared.fs,
|
||||
cx.path("light.css"),
|
||||
static_files::themes::LIGHT,
|
||||
options.enable_minification,
|
||||
)?,
|
||||
"dark" => write_minify(
|
||||
&cx.shared.fs,
|
||||
cx.path("dark.css"),
|
||||
static_files::themes::DARK,
|
||||
options.enable_minification,
|
||||
)?,
|
||||
"ayu" => write_minify(
|
||||
&cx.shared.fs,
|
||||
cx.path("ayu.css"),
|
||||
static_files::themes::AYU,
|
||||
options.enable_minification,
|
||||
)?,
|
||||
_ => {
|
||||
// Handle added third-party themes
|
||||
let content = try_err!(fs::read(&entry.path), &entry.path);
|
||||
cx.shared
|
||||
.fs
|
||||
.write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
|
||||
}
|
||||
};
|
||||
|
||||
themes.insert(theme.to_owned());
|
||||
}
|
||||
|
||||
|
|
@ -634,20 +684,6 @@ fn write_shared(
|
|||
write(cx.path("brush.svg"), static_files::BRUSH_SVG)?;
|
||||
write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?;
|
||||
write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?;
|
||||
write_minify(
|
||||
&cx.shared.fs,
|
||||
cx.path("light.css"),
|
||||
static_files::themes::LIGHT,
|
||||
options.enable_minification,
|
||||
)?;
|
||||
themes.insert("light".to_owned());
|
||||
write_minify(
|
||||
&cx.shared.fs,
|
||||
cx.path("dark.css"),
|
||||
static_files::themes::DARK,
|
||||
options.enable_minification,
|
||||
)?;
|
||||
themes.insert("dark".to_owned());
|
||||
|
||||
let mut themes: Vec<&String> = themes.iter().collect();
|
||||
themes.sort();
|
||||
|
|
@ -958,7 +994,7 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
})
|
||||
.collect::<String>()
|
||||
);
|
||||
let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.themes);
|
||||
let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
|
||||
cx.shared.fs.write(&dst, v.as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
|
@ -1376,7 +1412,7 @@ impl Context {
|
|||
&page,
|
||||
sidebar,
|
||||
|buf: &mut Buffer| all.print(buf),
|
||||
&self.shared.themes,
|
||||
&self.shared.style_files,
|
||||
);
|
||||
self.shared.fs.write(&final_file, v.as_bytes())?;
|
||||
|
||||
|
|
@ -1385,9 +1421,9 @@ impl Context {
|
|||
page.description = "Settings of Rustdoc";
|
||||
page.root_path = "./";
|
||||
|
||||
let mut themes = self.shared.themes.clone();
|
||||
let mut style_files = self.shared.style_files.clone();
|
||||
let sidebar = "<p class='location'>Settings</p><div class='sidebar-elems'></div>";
|
||||
themes.push(PathBuf::from("settings.css"));
|
||||
style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
|
||||
let v = layout::render(
|
||||
&self.shared.layout,
|
||||
&page,
|
||||
|
|
@ -1396,7 +1432,7 @@ impl Context {
|
|||
self.shared.static_root_path.as_deref().unwrap_or("./"),
|
||||
&self.shared.resource_suffix,
|
||||
),
|
||||
&themes,
|
||||
&style_files,
|
||||
);
|
||||
self.shared.fs.write(&settings_file, v.as_bytes())?;
|
||||
|
||||
|
|
@ -1458,7 +1494,7 @@ impl Context {
|
|||
&page,
|
||||
|buf: &mut _| print_sidebar(self, it, buf),
|
||||
|buf: &mut _| print_item(self, it, buf),
|
||||
&self.shared.themes,
|
||||
&self.shared.style_files,
|
||||
)
|
||||
} else {
|
||||
let mut url = self.root_path();
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ impl<'a> SourceCollector<'a> {
|
|||
&page,
|
||||
"",
|
||||
|buf: &mut _| print_src(buf, &contents),
|
||||
&self.scx.themes,
|
||||
&self.scx.style_files,
|
||||
);
|
||||
self.scx.fs.write(&cur, v.as_bytes())?;
|
||||
self.scx.local_sources.insert(p, href);
|
||||
|
|
|
|||
561
src/librustdoc/html/static/themes/ayu.css
Normal file
561
src/librustdoc/html/static/themes/ayu.css
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
Based off of the Ayu theme
|
||||
Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
*/
|
||||
|
||||
/* General structure and fonts */
|
||||
|
||||
body {
|
||||
background-color: #0f1419;
|
||||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
|
||||
color: white;
|
||||
}
|
||||
h1.fqn {
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
h1.fqn a {
|
||||
color: #fff;
|
||||
}
|
||||
h2, h3:not(.impl):not(.method):not(.type):not(.tymethod) {
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.in-band {
|
||||
background-color: #0f1419;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
code {
|
||||
color: #ffb454;
|
||||
}
|
||||
h3 > code, h4 > code, h5 > code {
|
||||
color: #e6e1cf;
|
||||
}
|
||||
pre > code {
|
||||
color: #e6e1cf;
|
||||
}
|
||||
span code {
|
||||
color: #e6e1cf;
|
||||
}
|
||||
.docblock a > code {
|
||||
color: #39AFD7 !important;
|
||||
}
|
||||
.docblock code, .docblock-short code {
|
||||
background-color: #191f26;
|
||||
}
|
||||
pre {
|
||||
color: #e6e1cf;
|
||||
background-color: #191f26;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-color: #14191f;
|
||||
}
|
||||
|
||||
/* Improve the scrollbar display on firefox */
|
||||
* {
|
||||
scrollbar-color: #5c6773 transparent;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
scrollbar-color: #5c6773 transparent;
|
||||
}
|
||||
|
||||
/* Improve the scrollbar display on webkit-based browsers */
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #5c6773;
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background-color: #5c6773;
|
||||
}
|
||||
|
||||
.sidebar .current {
|
||||
background-color: transparent;
|
||||
color: #ffb44c;
|
||||
}
|
||||
|
||||
.source .sidebar {
|
||||
background-color: #0f1419;
|
||||
}
|
||||
|
||||
.sidebar .location {
|
||||
border-color: #000;
|
||||
background-color: #0f1419;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar-elems .location {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.sidebar-elems .location a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar .version {
|
||||
border-bottom-color: #DDD;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
border-top-color: #5c6773;
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
|
||||
.block a:hover {
|
||||
background: transparent;
|
||||
color: #ffb44c;
|
||||
}
|
||||
|
||||
.line-numbers span { color: #5c6773ab; }
|
||||
.line-numbers .line-highlighted {
|
||||
background-color: rgba(255, 236, 164, 0.06) !important;
|
||||
padding-right: 4px;
|
||||
border-right: 1px solid #ffb44c;
|
||||
}
|
||||
|
||||
.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
|
||||
.docblock table, .docblock table td, .docblock table th {
|
||||
border-color: #5c6773;
|
||||
}
|
||||
|
||||
.content .method .where,
|
||||
.content .fn .where,
|
||||
.content .where.fmt-newline {
|
||||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
.content .highlighted {
|
||||
color: #000 !important;
|
||||
background-color: #c6afb3;
|
||||
}
|
||||
.content .highlighted a, .content .highlighted span { color: #000 !important; }
|
||||
.content .highlighted {
|
||||
background-color: #c6afb3;
|
||||
}
|
||||
.search-results a {
|
||||
color: #0096cf;
|
||||
}
|
||||
.search-results a span.desc {
|
||||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
.content .stability::before { color: #ccc; }
|
||||
|
||||
.content span.foreigntype, .content a.foreigntype { color: #ef57ff; }
|
||||
.content span.union, .content a.union { color: #98a01c; }
|
||||
.content span.constant, .content a.constant,
|
||||
.content span.static, .content a.static { color: #6380a0; }
|
||||
.content span.primitive, .content a.primitive { color: #32889b; }
|
||||
.content span.traitalias, .content a.traitalias { color: #57d399; }
|
||||
.content span.keyword, .content a.keyword { color: #de5249; }
|
||||
|
||||
.content span.externcrate, .content span.mod, .content a.mod {
|
||||
color: #acccf9;
|
||||
}
|
||||
.content span.struct, .content a.struct {
|
||||
color: #ffa0a5;
|
||||
}
|
||||
.content span.enum, .content a.enum {
|
||||
color: #99e0c9;
|
||||
}
|
||||
.content span.trait, .content a.trait {
|
||||
color: #39AFD7;
|
||||
}
|
||||
.content span.type, .content a.type {
|
||||
color: #cfbcf5;
|
||||
}
|
||||
.content span.fn, .content a.fn, .content span.method,
|
||||
.content a.method, .content span.tymethod,
|
||||
.content a.tymethod, .content .fnname {
|
||||
color: #fdd687;
|
||||
}
|
||||
.content span.attr, .content a.attr, .content span.derive,
|
||||
.content a.derive, .content span.macro, .content a.macro {
|
||||
color: #a37acc;
|
||||
}
|
||||
|
||||
pre.rust .comment, pre.rust .doccomment {
|
||||
color: #788797;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
nav:not(.sidebar) {
|
||||
border-bottom-color: #e0e0e0;
|
||||
}
|
||||
nav.main .current {
|
||||
border-top-color: #5c6773;
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
nav.main .separator {
|
||||
border: 1px solid #5c6773;
|
||||
}
|
||||
a {
|
||||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow),
|
||||
.docblock-short a:not(.srclink):not(.test-arrow), .stability a {
|
||||
color: #39AFD7;
|
||||
}
|
||||
|
||||
.stab.internal a {
|
||||
color: #304FFE;
|
||||
}
|
||||
|
||||
.collapse-toggle {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#crate-search {
|
||||
color: #c5c5c5;
|
||||
background-color: #141920;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
border-color: #5c6773;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
color: #ffffff;
|
||||
background-color: #141920;
|
||||
box-shadow: none;
|
||||
transition: box-shadow 150ms ease-in-out;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#crate-search+.search-input:focus {
|
||||
box-shadow: 0px 6px 20px 0px black;
|
||||
}
|
||||
|
||||
.search-focus:disabled {
|
||||
color: #929292;
|
||||
}
|
||||
|
||||
.module-item .stab {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.stab.unstable,
|
||||
.stab.internal,
|
||||
.stab.deprecated,
|
||||
.stab.portability {
|
||||
color: #c5c5c5;
|
||||
background: #314559 !important;
|
||||
border-style: none !important;
|
||||
border-radius: 4px;
|
||||
padding: 3px 6px 3px 6px;
|
||||
}
|
||||
|
||||
.stab.portability > code {
|
||||
color: #e6e1cf;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#help > div {
|
||||
background: #14191f;
|
||||
box-shadow: 0px 6px 20px 0px black;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.since {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
tr.result span.primitive::after, tr.result span.keyword::after {
|
||||
color: #788797;
|
||||
}
|
||||
|
||||
.line-numbers :target { background-color: transparent; }
|
||||
|
||||
/* Code highlighting */
|
||||
pre.rust .number, pre.rust .string { color: #b8cc52; }
|
||||
pre.rust .kw, pre.rust .kw-2, pre.rust .prelude-ty,
|
||||
pre.rust .bool-val, pre.rust .prelude-val,
|
||||
pre.rust .op, pre.rust .lifetime { color: #ff7733; }
|
||||
pre.rust .macro, pre.rust .macro-nonterminal { color: #a37acc; }
|
||||
pre.rust .question-mark {
|
||||
color: #ff9011;
|
||||
}
|
||||
pre.rust .self {
|
||||
color: #36a3d9;
|
||||
font-style: italic;
|
||||
}
|
||||
pre.rust .attribute {
|
||||
color: #e6e1cf;
|
||||
}
|
||||
pre.rust .attribute .ident, pre.rust .attribute .op {
|
||||
color: #e6e1cf;
|
||||
}
|
||||
|
||||
.example-wrap > pre.line-number {
|
||||
color: #5c67736e;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a.test-arrow {
|
||||
font-size: 100%;
|
||||
color: #788797;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
a.test-arrow:hover {
|
||||
background-color: rgba(242, 151, 24, 0.05);
|
||||
color: #ffb44c;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
:target > code, :target > .in-band {
|
||||
background: rgba(255, 236, 164, 0.06);
|
||||
border-right: 3px solid #ffb44c;
|
||||
}
|
||||
|
||||
pre.compile_fail {
|
||||
border-left: 2px solid rgba(255,0,0,.4);
|
||||
}
|
||||
|
||||
pre.compile_fail:hover, .information:hover + pre.compile_fail {
|
||||
border-left: 2px solid #f00;
|
||||
}
|
||||
|
||||
pre.should_panic {
|
||||
border-left: 2px solid rgba(255,0,0,.4);
|
||||
}
|
||||
|
||||
pre.should_panic:hover, .information:hover + pre.should_panic {
|
||||
border-left: 2px solid #f00;
|
||||
}
|
||||
|
||||
pre.ignore {
|
||||
border-left: 2px solid rgba(255,142,0,.6);
|
||||
}
|
||||
|
||||
pre.ignore:hover, .information:hover + pre.ignore {
|
||||
border-left: 2px solid #ff9200;
|
||||
}
|
||||
|
||||
.tooltip.compile_fail {
|
||||
color: rgba(255,0,0,.5);
|
||||
}
|
||||
|
||||
.information > .compile_fail:hover {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.tooltip.should_panic {
|
||||
color: rgba(255,0,0,.5);
|
||||
}
|
||||
|
||||
.information > .should_panic:hover {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.tooltip.ignore {
|
||||
color: rgba(255,142,0,.6);
|
||||
}
|
||||
|
||||
.information > .ignore:hover {
|
||||
color: #ff9200;
|
||||
}
|
||||
|
||||
.search-failed a {
|
||||
color: #39AFD7;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
background-color: #314559;
|
||||
color: #c5c5c5;
|
||||
border: 1px solid #5c6773;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext::after {
|
||||
border-color: transparent #314559 transparent transparent;
|
||||
}
|
||||
|
||||
#titles > div.selected {
|
||||
background-color: #141920 !important;
|
||||
border-bottom: 1px solid #ffb44c !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
#titles > div:not(.selected) {
|
||||
background-color: transparent !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#titles > div:hover {
|
||||
border-bottom: 1px solid rgba(242, 151, 24, 0.3);
|
||||
}
|
||||
|
||||
#titles > div > div.count {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* rules that this theme does not need to set, here to satisfy the rule checker */
|
||||
/* note that a lot of these are partially set in some way (meaning they are set
|
||||
individually rather than as a group) */
|
||||
/* TODO: these rules should be at the bottom of the file but currently must be
|
||||
above the `@media (max-width: 700px)` rules due to a bug in the css checker */
|
||||
/* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */
|
||||
.content .highlighted.mod, .content .highlighted.externcrate {}
|
||||
.search-input:focus {}
|
||||
.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {}
|
||||
.content .highlighted.trait {}
|
||||
.content span.struct,.content a.struct,.block a.current.struct {}
|
||||
#titles>div:hover,#titles>div.selected {}
|
||||
.content .highlighted.traitalias {}
|
||||
.content span.type,.content a.type,.block a.current.type {}
|
||||
.content span.union,.content a.union,.block a.current.union {}
|
||||
.content .highlighted.foreigntype {}
|
||||
pre.rust .lifetime {}
|
||||
.content .highlighted.primitive {}
|
||||
.content .highlighted.constant,.content .highlighted.static {}
|
||||
.stab.unstable {}
|
||||
.content .highlighted.fn,.content .highlighted.method,.content .highlighted.tymethod {}
|
||||
h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod) {}
|
||||
.content span.enum,.content a.enum,.block a.current.enum {}
|
||||
.content span.constant,.content a.constant,.block a.current.constant,.content span.static,.content a.static,.block a.current.static {}
|
||||
.content span.keyword,.content a.keyword,.block a.current.keyword {}
|
||||
pre.rust .comment {}
|
||||
.content .highlighted.enum {}
|
||||
.content .highlighted.struct {}
|
||||
.content .highlighted.keyword {}
|
||||
.content span.traitalias,.content a.traitalias,.block a.current.traitalias {}
|
||||
.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,.content .fnname {}
|
||||
pre.rust .kw {}
|
||||
pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute,pre.rust .attribute .ident {}
|
||||
.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {}
|
||||
pre.rust .doccomment {}
|
||||
.stab.deprecated {}
|
||||
.content .highlighted.attr,.content .highlighted.derive,.content .highlighted.macro {}
|
||||
.stab.portability {}
|
||||
.content .highlighted.union {}
|
||||
.content span.primitive,.content a.primitive,.block a.current.primitive {}
|
||||
.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod {}
|
||||
.content .highlighted.type {}
|
||||
pre.rust .kw-2,pre.rust .prelude-ty {}
|
||||
.content span.trait,.content a.trait,.block a.current.trait {}
|
||||
.stab.internal {}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.sidebar-menu {
|
||||
background-color: #14191f;
|
||||
border-bottom-color: #5c6773;
|
||||
border-right-color: #5c6773;
|
||||
}
|
||||
|
||||
.sidebar-elems {
|
||||
background-color: #14191f;
|
||||
border-right-color: #5c6773;
|
||||
}
|
||||
|
||||
#sidebar-filler {
|
||||
background-color: #14191f;
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
}
|
||||
|
||||
kbd {
|
||||
color: #c5c5c5;
|
||||
background-color: #314559;
|
||||
border-color: #5c6773;
|
||||
border-bottom-color: #5c6773;
|
||||
box-shadow-color: #c6cbd1;
|
||||
}
|
||||
|
||||
#theme-picker, #settings-menu {
|
||||
border-color: #5c6773;
|
||||
background-color: #0f1419;
|
||||
}
|
||||
|
||||
#theme-picker > img, #settings-menu > img {
|
||||
filter: invert(100);
|
||||
}
|
||||
|
||||
#theme-picker:hover, #theme-picker:focus,
|
||||
#settings-menu:hover, #settings-menu:focus {
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
#theme-choices {
|
||||
border-color: #5c6773;
|
||||
background-color: #0f1419;
|
||||
}
|
||||
|
||||
#theme-choices > button:not(:first-child) {
|
||||
border-top-color: #c5c5c5;
|
||||
}
|
||||
|
||||
#theme-choices > button:hover, #theme-choices > button:focus {
|
||||
background-color: rgba(70, 70, 70, 0.33);
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#theme-picker {
|
||||
background: #0f1419;
|
||||
}
|
||||
}
|
||||
|
||||
#all-types {
|
||||
background-color: #14191f;
|
||||
}
|
||||
#all-types:hover {
|
||||
background-color: rgba(70, 70, 70, 0.33);
|
||||
}
|
||||
|
||||
.search-results td span.alias {
|
||||
color: #c5c5c5;
|
||||
}
|
||||
.search-results td span.grey {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#sidebar-toggle {
|
||||
background-color: #14191f;
|
||||
}
|
||||
#sidebar-toggle:hover {
|
||||
background-color: rgba(70, 70, 70, 0.33);
|
||||
}
|
||||
#source-sidebar {
|
||||
background-color: #14191f;
|
||||
}
|
||||
#source-sidebar > .title {
|
||||
color: #fff;
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
div.files > a:hover, div.name:hover {
|
||||
background-color: #14191f;
|
||||
color: #ffb44c;
|
||||
}
|
||||
div.files > .selected {
|
||||
background-color: #14191f;
|
||||
color: #ffb44c;
|
||||
}
|
||||
.setting-line > .title {
|
||||
border-bottom-color: #5c6773;
|
||||
}
|
||||
input:checked + .slider {
|
||||
background-color: #ffb454 !important;
|
||||
}
|
||||
|
|
@ -64,6 +64,9 @@ pub mod themes {
|
|||
|
||||
/// The "dark" theme.
|
||||
pub static DARK: &str = include_str!("static/themes/dark.css");
|
||||
|
||||
/// The "ayu" theme.
|
||||
pub static AYU: &str = include_str!("static/themes/ayu.css");
|
||||
}
|
||||
|
||||
/// Files related to the Fira Sans font.
|
||||
|
|
|
|||
|
|
@ -242,8 +242,8 @@
|
|||
#![feature(atomic_mut_ptr)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(cfg_accessible)]
|
||||
#![feature(can_vector)]
|
||||
#![feature(cfg_accessible)]
|
||||
#![feature(cfg_target_has_atomic)]
|
||||
#![feature(cfg_target_thread_local)]
|
||||
#![feature(char_error_internals)]
|
||||
|
|
@ -276,8 +276,8 @@
|
|||
#![feature(hashmap_internals)]
|
||||
#![feature(int_error_internals)]
|
||||
#![feature(int_error_matching)]
|
||||
#![feature(into_future)]
|
||||
#![feature(integer_atomics)]
|
||||
#![feature(into_future)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(libc)]
|
||||
#![feature(link_args)]
|
||||
|
|
@ -286,6 +286,7 @@
|
|||
#![feature(log_syntax)]
|
||||
#![feature(maybe_uninit_ref)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(needs_panic_runtime)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
|
|
@ -305,7 +306,7 @@
|
|||
#![feature(shrink_to)]
|
||||
#![feature(slice_concat_ext)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(slice_strip)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(stdsimd)]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ pub mod usercalls;
|
|||
#[cfg(not(test))]
|
||||
global_asm!(include_str!("entry.S"));
|
||||
|
||||
#[repr(C)]
|
||||
struct EntryReturn(u64, u64);
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tcs_init(secondary: bool) {
|
||||
|
|
@ -56,8 +59,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
|
|||
// able to specify this
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
|
||||
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
|
||||
// FIXME: how to support TLS in library mode?
|
||||
let tls = Box::new(tls::Tls::new());
|
||||
let _tls_guard = unsafe { tls.activate() };
|
||||
|
|
@ -65,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64
|
|||
if secondary {
|
||||
super::thread::Thread::entry();
|
||||
|
||||
(0, 0)
|
||||
EntryReturn(0, 0)
|
||||
} else {
|
||||
extern "C" {
|
||||
fn main(argc: isize, argv: *const *const u8) -> isize;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,16 @@ use crate::ffi::OsStr;
|
|||
use crate::mem;
|
||||
use crate::path::Prefix;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "\\";
|
||||
pub const MAIN_SEP: char = '\\';
|
||||
|
||||
// The unsafety here stems from converting between `&OsStr` and `&[u8]`
|
||||
// and back. This is safe to do because (1) we only look at ASCII
|
||||
// contents of the encoding and (2) new &OsStr values are produced
|
||||
// only from ASCII-bounded slices of existing &OsStr values.
|
||||
fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
|
||||
unsafe { mem::transmute(s) }
|
||||
}
|
||||
|
|
@ -19,76 +29,79 @@ pub fn is_verbatim_sep(b: u8) -> bool {
|
|||
b == b'\\'
|
||||
}
|
||||
|
||||
pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
|
||||
use crate::path::Prefix::*;
|
||||
unsafe {
|
||||
// The unsafety here stems from converting between &OsStr and &[u8]
|
||||
// and back. This is safe to do because (1) we only look at ASCII
|
||||
// contents of the encoding and (2) new &OsStr values are produced
|
||||
// only from ASCII-bounded slices of existing &OsStr values.
|
||||
let mut path = os_str_as_u8_slice(path);
|
||||
|
||||
if path.starts_with(br"\\") {
|
||||
// \\
|
||||
path = &path[2..];
|
||||
if path.starts_with(br"?\") {
|
||||
// \\?\
|
||||
path = &path[2..];
|
||||
if path.starts_with(br"UNC\") {
|
||||
// \\?\UNC\server\share
|
||||
path = &path[4..];
|
||||
let (server, share) = match parse_two_comps(path, is_verbatim_sep) {
|
||||
Some((server, share)) => {
|
||||
(u8_slice_as_os_str(server), u8_slice_as_os_str(share))
|
||||
}
|
||||
None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])),
|
||||
};
|
||||
return Some(VerbatimUNC(server, share));
|
||||
} else {
|
||||
// \\?\path
|
||||
let idx = path.iter().position(|&b| b == b'\\');
|
||||
if idx == Some(2) && path[1] == b':' {
|
||||
let c = path[0];
|
||||
if c.is_ascii() && (c as char).is_alphabetic() {
|
||||
// \\?\C:\ path
|
||||
return Some(VerbatimDisk(c.to_ascii_uppercase()));
|
||||
}
|
||||
}
|
||||
let slice = &path[..idx.unwrap_or(path.len())];
|
||||
return Some(Verbatim(u8_slice_as_os_str(slice)));
|
||||
}
|
||||
} else if path.starts_with(b".\\") {
|
||||
// \\.\path
|
||||
path = &path[2..];
|
||||
let pos = path.iter().position(|&b| b == b'\\');
|
||||
let slice = &path[..pos.unwrap_or(path.len())];
|
||||
return Some(DeviceNS(u8_slice_as_os_str(slice)));
|
||||
}
|
||||
match parse_two_comps(path, is_sep_byte) {
|
||||
Some((server, share)) if !server.is_empty() && !share.is_empty() => {
|
||||
// \\server\share
|
||||
return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if path.get(1) == Some(&b':') {
|
||||
// C:
|
||||
let c = path[0];
|
||||
if c.is_ascii() && (c as char).is_alphabetic() {
|
||||
return Some(Disk(c.to_ascii_uppercase()));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
|
||||
let first = &path[..path.iter().position(|x| f(*x))?];
|
||||
path = &path[(first.len() + 1)..];
|
||||
let idx = path.iter().position(|x| f(*x));
|
||||
let second = &path[..idx.unwrap_or(path.len())];
|
||||
Some((first, second))
|
||||
}
|
||||
// In most DOS systems, it is not possible to have more than 26 drive letters.
|
||||
// See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
|
||||
pub fn is_valid_drive_letter(disk: u8) -> bool {
|
||||
disk.is_ascii_alphabetic()
|
||||
}
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "\\";
|
||||
pub const MAIN_SEP: char = '\\';
|
||||
pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
|
||||
use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
|
||||
|
||||
let path = os_str_as_u8_slice(path);
|
||||
|
||||
// \\
|
||||
if let Some(path) = path.strip_prefix(br"\\") {
|
||||
// \\?\
|
||||
if let Some(path) = path.strip_prefix(br"?\") {
|
||||
// \\?\UNC\server\share
|
||||
if let Some(path) = path.strip_prefix(br"UNC\") {
|
||||
let (server, share) = match get_first_two_components(path, is_verbatim_sep) {
|
||||
Some((server, share)) => unsafe {
|
||||
(u8_slice_as_os_str(server), u8_slice_as_os_str(share))
|
||||
},
|
||||
None => (unsafe { u8_slice_as_os_str(path) }, OsStr::new("")),
|
||||
};
|
||||
return Some(VerbatimUNC(server, share));
|
||||
} else {
|
||||
// \\?\path
|
||||
match path {
|
||||
// \\?\C:\path
|
||||
[c, b':', b'\\', ..] if is_valid_drive_letter(*c) => {
|
||||
return Some(VerbatimDisk(c.to_ascii_uppercase()));
|
||||
}
|
||||
// \\?\cat_pics
|
||||
_ => {
|
||||
let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
|
||||
let slice = &path[..idx];
|
||||
return Some(Verbatim(unsafe { u8_slice_as_os_str(slice) }));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(path) = path.strip_prefix(b".\\") {
|
||||
// \\.\COM42
|
||||
let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
|
||||
let slice = &path[..idx];
|
||||
return Some(DeviceNS(unsafe { u8_slice_as_os_str(slice) }));
|
||||
}
|
||||
match get_first_two_components(path, is_sep_byte) {
|
||||
Some((server, share)) if !server.is_empty() && !share.is_empty() => {
|
||||
// \\server\share
|
||||
return Some(unsafe { UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)) });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let [c, b':', ..] = path {
|
||||
// C:
|
||||
if is_valid_drive_letter(*c) {
|
||||
return Some(Disk(c.to_ascii_uppercase()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the first two path components with predicate `f`.
|
||||
///
|
||||
/// The two components returned will be use by caller
|
||||
/// to construct `VerbatimUNC` or `UNC` Windows path prefix.
|
||||
///
|
||||
/// Returns [`None`] if there are no separators in path.
|
||||
fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
|
||||
let idx = path.iter().position(|&x| f(x))?;
|
||||
// Panic safe
|
||||
// The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index.
|
||||
let (first, path) = (&path[..idx], &path[idx + 1..]);
|
||||
let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len());
|
||||
let second = &path[..idx];
|
||||
Some((first, second))
|
||||
}
|
||||
|
|
|
|||
21
src/libstd/sys/windows/path/tests.rs
Normal file
21
src/libstd/sys/windows/path/tests.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_first_two_components() {
|
||||
assert_eq!(
|
||||
get_first_two_components(br"server\share", is_verbatim_sep),
|
||||
Some((&b"server"[..], &b"share"[..])),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_first_two_components(br"server\", is_verbatim_sep),
|
||||
Some((&b"server"[..], &b""[..]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_first_two_components(br"\server\", is_verbatim_sep),
|
||||
Some((&b""[..], &b"server"[..]))
|
||||
);
|
||||
|
||||
assert_eq!(get_first_two_components(br"there are no separators here", is_verbatim_sep), None,);
|
||||
}
|
||||
|
|
@ -641,9 +641,8 @@ where
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn current() -> Thread {
|
||||
thread_info::current_thread().expect(
|
||||
"use of std::thread::current() is not \
|
||||
possible after the thread's local \
|
||||
data has been destroyed",
|
||||
"use of std::thread::current() is not possible \
|
||||
after the thread's local data has been destroyed",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit d134a53927fa033ae7e0f3e8ee872ff2dc71468d
|
||||
Subproject commit 86b120e6f302d39cd6973b6391fb299d7bc22122
|
||||
6
src/test/ui/issues/issue-73886.rs
Normal file
6
src/test/ui/issues/issue-73886.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let _ = &&[0] as &[_];
|
||||
//~^ ERROR non-primitive cast: `&&[i32; 1]` as `&[_]`
|
||||
let _ = 7u32 as Option<_>;
|
||||
//~^ ERROR non-primitive cast: `u32` as `std::option::Option<_>`
|
||||
}
|
||||
15
src/test/ui/issues/issue-73886.stderr
Normal file
15
src/test/ui/issues/issue-73886.stderr
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
error[E0605]: non-primitive cast: `&&[i32; 1]` as `&[_]`
|
||||
--> $DIR/issue-73886.rs:2:13
|
||||
|
|
||||
LL | let _ = &&[0] as &[_];
|
||||
| ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
||||
|
||||
error[E0605]: non-primitive cast: `u32` as `std::option::Option<_>`
|
||||
--> $DIR/issue-73886.rs:4:13
|
||||
|
|
||||
LL | let _ = 7u32 as Option<_>;
|
||||
| ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0605`.
|
||||
4
src/test/ui/issues/issue-74086.rs
Normal file
4
src/test/ui/issues/issue-74086.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
static BUG: fn(_) -> u8 = |_| 8;
|
||||
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121]
|
||||
}
|
||||
12
src/test/ui/issues/issue-74086.stderr
Normal file
12
src/test/ui/issues/issue-74086.stderr
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/issue-74086.rs:2:20
|
||||
|
|
||||
LL | static BUG: fn(_) -> u8 = |_| 8;
|
||||
| ^
|
||||
| |
|
||||
| not allowed in type signatures
|
||||
| help: use type parameters instead: `T`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0121`.
|
||||
|
|
@ -2,8 +2,7 @@ struct S(usize, usize, usize, usize);
|
|||
|
||||
fn main() {
|
||||
if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
||||
//~^ ERROR struct `S` does not have fields named `a`, `b`, `c`, `d` [E0026]
|
||||
//~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
|
||||
//~^ ERROR tuple variant `S` written as struct variant
|
||||
println!("hi");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
|
||||
--> $DIR/missing-fields-in-struct-pattern.rs:4:16
|
||||
|
|
||||
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
||||
| ^ ^ ^ ^ struct `S` does not have these fields
|
||||
|
||||
error[E0027]: pattern does not mention fields `0`, `1`, `2`, `3`
|
||||
error[E0769]: tuple variant `S` written as struct variant
|
||||
--> $DIR/missing-fields-in-struct-pattern.rs:4:12
|
||||
|
|
||||
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
||||
| ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
|
||||
|
|
||||
= note: trying to match a tuple variant with a struct variant pattern
|
||||
| ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
Some errors have detailed explanations: E0026, E0027.
|
||||
For more information about an error, try `rustc --explain E0026`.
|
||||
For more information about this error, try `rustc --explain E0769`.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ enum X {
|
|||
|
||||
fn main() {
|
||||
match X::Y(0) {
|
||||
X::Y { number } => {} //~ ERROR does not have a field named `number`
|
||||
//~^ ERROR pattern does not mention field `0`
|
||||
X::Y { number } => {}
|
||||
//~^ ERROR tuple variant `X::Y` written as struct variant
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
error[E0026]: variant `X::Y` does not have a field named `number`
|
||||
--> $DIR/issue-41314.rs:7:16
|
||||
|
|
||||
LL | X::Y { number } => {}
|
||||
| ^^^^^^ variant `X::Y` does not have this field
|
||||
|
||||
error[E0027]: pattern does not mention field `0`
|
||||
error[E0769]: tuple variant `X::Y` written as struct variant
|
||||
--> $DIR/issue-41314.rs:7:9
|
||||
|
|
||||
LL | X::Y { number } => {}
|
||||
| ^^^^^^^^^^^^^^^ missing field `0`
|
||||
|
|
||||
= note: trying to match a tuple variant with a struct variant pattern
|
||||
| ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
Some errors have detailed explanations: E0026, E0027.
|
||||
For more information about an error, try `rustc --explain E0026`.
|
||||
For more information about this error, try `rustc --explain E0769`.
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ fn test7(x: _) { let _x: usize = x; }
|
|||
|
||||
fn test8(_f: fn() -> _) { }
|
||||
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
|
||||
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures
|
||||
|
||||
struct Test9;
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ pub fn main() {
|
|||
|
||||
fn fn_test8(_f: fn() -> _) { }
|
||||
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
|
||||
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures
|
||||
|
||||
struct FnTest9;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
error: expected identifier, found reserved identifier `_`
|
||||
--> $DIR/typeck_type_placeholder_item.rs:152:18
|
||||
--> $DIR/typeck_type_placeholder_item.rs:154:18
|
||||
|
|
||||
LL | struct BadStruct<_>(_);
|
||||
| ^ expected identifier, found reserved identifier
|
||||
|
||||
error: expected identifier, found reserved identifier `_`
|
||||
--> $DIR/typeck_type_placeholder_item.rs:155:16
|
||||
--> $DIR/typeck_type_placeholder_item.rs:157:16
|
||||
|
|
||||
LL | trait BadTrait<_> {}
|
||||
| ^ expected identifier, found reserved identifier
|
||||
|
||||
error: expected identifier, found reserved identifier `_`
|
||||
--> $DIR/typeck_type_placeholder_item.rs:165:19
|
||||
--> $DIR/typeck_type_placeholder_item.rs:167:19
|
||||
|
|
||||
LL | struct BadStruct1<_, _>(_);
|
||||
| ^ expected identifier, found reserved identifier
|
||||
|
||||
error: expected identifier, found reserved identifier `_`
|
||||
--> $DIR/typeck_type_placeholder_item.rs:165:22
|
||||
--> $DIR/typeck_type_placeholder_item.rs:167:22
|
||||
|
|
||||
LL | struct BadStruct1<_, _>(_);
|
||||
| ^ expected identifier, found reserved identifier
|
||||
|
||||
error: expected identifier, found reserved identifier `_`
|
||||
--> $DIR/typeck_type_placeholder_item.rs:170:19
|
||||
--> $DIR/typeck_type_placeholder_item.rs:172:19
|
||||
|
|
||||
LL | struct BadStruct2<_, T>(_, T);
|
||||
| ^ expected identifier, found reserved identifier
|
||||
|
||||
error: associated constant in `impl` without body
|
||||
--> $DIR/typeck_type_placeholder_item.rs:201:5
|
||||
--> $DIR/typeck_type_placeholder_item.rs:203:5
|
||||
|
|
||||
LL | const C: _;
|
||||
| ^^^^^^^^^^-
|
||||
|
|
@ -37,7 +37,7 @@ LL | const C: _;
|
|||
| help: provide a definition for the constant: `= <expr>;`
|
||||
|
||||
error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters
|
||||
--> $DIR/typeck_type_placeholder_item.rs:165:22
|
||||
--> $DIR/typeck_type_placeholder_item.rs:167:22
|
||||
|
|
||||
LL | struct BadStruct1<_, _>(_);
|
||||
| - ^ already used
|
||||
|
|
@ -131,6 +131,15 @@ help: use type parameters instead
|
|||
LL | fn test7<T>(x: T) { let _x: usize = x; }
|
||||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:33:22
|
||||
|
|
||||
LL | fn test8(_f: fn() -> _) { }
|
||||
| ^
|
||||
| |
|
||||
| not allowed in type signatures
|
||||
| help: use type parameters instead: `T`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:33:22
|
||||
|
|
||||
|
|
@ -143,7 +152,7 @@ LL | fn test8<T>(_f: fn() -> T) { }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:46:26
|
||||
--> $DIR/typeck_type_placeholder_item.rs:47:26
|
||||
|
|
||||
LL | fn test11(x: &usize) -> &_ {
|
||||
| -^
|
||||
|
|
@ -152,7 +161,7 @@ LL | fn test11(x: &usize) -> &_ {
|
|||
| help: replace with the correct return type: `&&usize`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:51:52
|
||||
--> $DIR/typeck_type_placeholder_item.rs:52:52
|
||||
|
|
||||
LL | unsafe fn test12(x: *const usize) -> *const *const _ {
|
||||
| --------------^
|
||||
|
|
@ -161,7 +170,7 @@ LL | unsafe fn test12(x: *const usize) -> *const *const _ {
|
|||
| help: replace with the correct return type: `*const *const usize`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:65:8
|
||||
--> $DIR/typeck_type_placeholder_item.rs:66:8
|
||||
|
|
||||
LL | a: _,
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -180,13 +189,13 @@ LL | b: (T, T),
|
|||
|
|
||||
|
||||
error: missing type for `static` item
|
||||
--> $DIR/typeck_type_placeholder_item.rs:71:12
|
||||
--> $DIR/typeck_type_placeholder_item.rs:72:12
|
||||
|
|
||||
LL | static A = 42;
|
||||
| ^ help: provide a type for the item: `A: i32`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:73:15
|
||||
--> $DIR/typeck_type_placeholder_item.rs:74:15
|
||||
|
|
||||
LL | static B: _ = 42;
|
||||
| ^
|
||||
|
|
@ -195,13 +204,13 @@ LL | static B: _ = 42;
|
|||
| help: replace `_` with the correct type: `i32`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:75:15
|
||||
--> $DIR/typeck_type_placeholder_item.rs:76:15
|
||||
|
|
||||
LL | static C: Option<_> = Some(42);
|
||||
| ^^^^^^^^^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:78:21
|
||||
--> $DIR/typeck_type_placeholder_item.rs:79:21
|
||||
|
|
||||
LL | fn fn_test() -> _ { 5 }
|
||||
| ^
|
||||
|
|
@ -210,7 +219,7 @@ LL | fn fn_test() -> _ { 5 }
|
|||
| help: replace with the correct return type: `i32`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:81:23
|
||||
--> $DIR/typeck_type_placeholder_item.rs:82:23
|
||||
|
|
||||
LL | fn fn_test2() -> (_, _) { (5, 5) }
|
||||
| -^--^-
|
||||
|
|
@ -220,7 +229,7 @@ LL | fn fn_test2() -> (_, _) { (5, 5) }
|
|||
| help: replace with the correct return type: `(i32, i32)`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:84:22
|
||||
--> $DIR/typeck_type_placeholder_item.rs:85:22
|
||||
|
|
||||
LL | static FN_TEST3: _ = "test";
|
||||
| ^
|
||||
|
|
@ -229,7 +238,7 @@ LL | static FN_TEST3: _ = "test";
|
|||
| help: replace `_` with the correct type: `&str`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:87:22
|
||||
--> $DIR/typeck_type_placeholder_item.rs:88:22
|
||||
|
|
||||
LL | static FN_TEST4: _ = 145;
|
||||
| ^
|
||||
|
|
@ -238,13 +247,13 @@ LL | static FN_TEST4: _ = 145;
|
|||
| help: replace `_` with the correct type: `i32`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:90:22
|
||||
--> $DIR/typeck_type_placeholder_item.rs:91:22
|
||||
|
|
||||
LL | static FN_TEST5: (_, _) = (1, 2);
|
||||
| ^^^^^^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:93:20
|
||||
--> $DIR/typeck_type_placeholder_item.rs:94:20
|
||||
|
|
||||
LL | fn fn_test6(_: _) { }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -255,7 +264,7 @@ LL | fn fn_test6<T>(_: T) { }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:96:20
|
||||
--> $DIR/typeck_type_placeholder_item.rs:97:20
|
||||
|
|
||||
LL | fn fn_test7(x: _) { let _x: usize = x; }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -266,7 +275,16 @@ LL | fn fn_test7<T>(x: T) { let _x: usize = x; }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:99:29
|
||||
--> $DIR/typeck_type_placeholder_item.rs:100:29
|
||||
|
|
||||
LL | fn fn_test8(_f: fn() -> _) { }
|
||||
| ^
|
||||
| |
|
||||
| not allowed in type signatures
|
||||
| help: use type parameters instead: `T`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:100:29
|
||||
|
|
||||
LL | fn fn_test8(_f: fn() -> _) { }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -277,7 +295,7 @@ LL | fn fn_test8<T>(_f: fn() -> T) { }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:121:12
|
||||
--> $DIR/typeck_type_placeholder_item.rs:123:12
|
||||
|
|
||||
LL | a: _,
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -296,13 +314,13 @@ LL | b: (T, T),
|
|||
|
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/typeck_type_placeholder_item.rs:126:18
|
||||
--> $DIR/typeck_type_placeholder_item.rs:128:18
|
||||
|
|
||||
LL | fn fn_test11(_: _) -> (_, _) { panic!() }
|
||||
| ^ cannot infer type
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:126:28
|
||||
--> $DIR/typeck_type_placeholder_item.rs:128:28
|
||||
|
|
||||
LL | fn fn_test11(_: _) -> (_, _) { panic!() }
|
||||
| ^ ^ not allowed in type signatures
|
||||
|
|
@ -310,7 +328,7 @@ LL | fn fn_test11(_: _) -> (_, _) { panic!() }
|
|||
| not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:130:30
|
||||
--> $DIR/typeck_type_placeholder_item.rs:132:30
|
||||
|
|
||||
LL | fn fn_test12(x: i32) -> (_, _) { (x, x) }
|
||||
| -^--^-
|
||||
|
|
@ -320,7 +338,7 @@ LL | fn fn_test12(x: i32) -> (_, _) { (x, x) }
|
|||
| help: replace with the correct return type: `(i32, i32)`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:133:33
|
||||
--> $DIR/typeck_type_placeholder_item.rs:135:33
|
||||
|
|
||||
LL | fn fn_test13(x: _) -> (i32, _) { (x, x) }
|
||||
| ------^-
|
||||
|
|
@ -329,7 +347,7 @@ LL | fn fn_test13(x: _) -> (i32, _) { (x, x) }
|
|||
| help: replace with the correct return type: `(i32, i32)`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:152:21
|
||||
--> $DIR/typeck_type_placeholder_item.rs:154:21
|
||||
|
|
||||
LL | struct BadStruct<_>(_);
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -340,7 +358,7 @@ LL | struct BadStruct<T>(T);
|
|||
| ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:157:15
|
||||
--> $DIR/typeck_type_placeholder_item.rs:159:15
|
||||
|
|
||||
LL | impl BadTrait<_> for BadStruct<_> {}
|
||||
| ^ ^ not allowed in type signatures
|
||||
|
|
@ -353,13 +371,13 @@ LL | impl<T> BadTrait<T> for BadStruct<T> {}
|
|||
| ^^^ ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:160:34
|
||||
--> $DIR/typeck_type_placeholder_item.rs:162:34
|
||||
|
|
||||
LL | fn impl_trait() -> impl BadTrait<_> {
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:165:25
|
||||
--> $DIR/typeck_type_placeholder_item.rs:167:25
|
||||
|
|
||||
LL | struct BadStruct1<_, _>(_);
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -370,7 +388,7 @@ LL | struct BadStruct1<T, _>(T);
|
|||
| ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:170:25
|
||||
--> $DIR/typeck_type_placeholder_item.rs:172:25
|
||||
|
|
||||
LL | struct BadStruct2<_, T>(_, T);
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -381,13 +399,13 @@ LL | struct BadStruct2<U, T>(U, T);
|
|||
| ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:174:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:176:14
|
||||
|
|
||||
LL | type X = Box<_>;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:42:27
|
||||
--> $DIR/typeck_type_placeholder_item.rs:43:27
|
||||
|
|
||||
LL | fn test10(&self, _x : _) { }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -398,7 +416,7 @@ LL | fn test10<T>(&self, _x : T) { }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:138:31
|
||||
--> $DIR/typeck_type_placeholder_item.rs:140:31
|
||||
|
|
||||
LL | fn method_test1(&self, x: _);
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -409,7 +427,7 @@ LL | fn method_test1<T>(&self, x: T);
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:140:31
|
||||
--> $DIR/typeck_type_placeholder_item.rs:142:31
|
||||
|
|
||||
LL | fn method_test2(&self, x: _) -> _;
|
||||
| ^ ^ not allowed in type signatures
|
||||
|
|
@ -422,7 +440,7 @@ LL | fn method_test2<T>(&self, x: T) -> T;
|
|||
| ^^^ ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:142:31
|
||||
--> $DIR/typeck_type_placeholder_item.rs:144:31
|
||||
|
|
||||
LL | fn method_test3(&self) -> _;
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -433,7 +451,7 @@ LL | fn method_test3<T>(&self) -> T;
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:144:26
|
||||
--> $DIR/typeck_type_placeholder_item.rs:146:26
|
||||
|
|
||||
LL | fn assoc_fn_test1(x: _);
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -444,7 +462,7 @@ LL | fn assoc_fn_test1<T>(x: T);
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:146:26
|
||||
--> $DIR/typeck_type_placeholder_item.rs:148:26
|
||||
|
|
||||
LL | fn assoc_fn_test2(x: _) -> _;
|
||||
| ^ ^ not allowed in type signatures
|
||||
|
|
@ -457,7 +475,7 @@ LL | fn assoc_fn_test2<T>(x: T) -> T;
|
|||
| ^^^ ^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:148:28
|
||||
--> $DIR/typeck_type_placeholder_item.rs:150:28
|
||||
|
|
||||
LL | fn assoc_fn_test3() -> _;
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -468,7 +486,7 @@ LL | fn assoc_fn_test3<T>() -> T;
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:60:37
|
||||
--> $DIR/typeck_type_placeholder_item.rs:61:37
|
||||
|
|
||||
LL | fn clone_from(&mut self, other: _) { *self = Test9; }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -479,7 +497,7 @@ LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:108:34
|
||||
--> $DIR/typeck_type_placeholder_item.rs:110:34
|
||||
|
|
||||
LL | fn fn_test10(&self, _x : _) { }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -490,7 +508,7 @@ LL | fn fn_test10<T>(&self, _x : T) { }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:116:41
|
||||
--> $DIR/typeck_type_placeholder_item.rs:118:41
|
||||
|
|
||||
LL | fn clone_from(&mut self, other: _) { *self = FnTest9; }
|
||||
| ^ not allowed in type signatures
|
||||
|
|
@ -501,25 +519,25 @@ LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
|
|||
| ^^^ ^
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:180:21
|
||||
--> $DIR/typeck_type_placeholder_item.rs:182:21
|
||||
|
|
||||
LL | type Y = impl Trait<_>;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:188:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:190:14
|
||||
|
|
||||
LL | type B = _;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:190:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:192:14
|
||||
|
|
||||
LL | const C: _;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:192:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:194:14
|
||||
|
|
||||
LL | const D: _ = 42;
|
||||
| ^
|
||||
|
|
@ -528,7 +546,7 @@ LL | const D: _ = 42;
|
|||
| help: replace `_` with the correct type: `i32`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:39:24
|
||||
--> $DIR/typeck_type_placeholder_item.rs:40:24
|
||||
|
|
||||
LL | fn test9(&self) -> _ { () }
|
||||
| ^
|
||||
|
|
@ -537,7 +555,7 @@ LL | fn test9(&self) -> _ { () }
|
|||
| help: replace with the correct return type: `()`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:57:24
|
||||
--> $DIR/typeck_type_placeholder_item.rs:58:24
|
||||
|
|
||||
LL | fn clone(&self) -> _ { Test9 }
|
||||
| ^
|
||||
|
|
@ -546,7 +564,7 @@ LL | fn clone(&self) -> _ { Test9 }
|
|||
| help: replace with the correct return type: `Test9`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:105:31
|
||||
--> $DIR/typeck_type_placeholder_item.rs:107:31
|
||||
|
|
||||
LL | fn fn_test9(&self) -> _ { () }
|
||||
| ^
|
||||
|
|
@ -555,7 +573,7 @@ LL | fn fn_test9(&self) -> _ { () }
|
|||
| help: replace with the correct return type: `()`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:113:28
|
||||
--> $DIR/typeck_type_placeholder_item.rs:115:28
|
||||
|
|
||||
LL | fn clone(&self) -> _ { FnTest9 }
|
||||
| ^
|
||||
|
|
@ -564,25 +582,25 @@ LL | fn clone(&self) -> _ { FnTest9 }
|
|||
| help: replace with the correct return type: `main::FnTest9`
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:197:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:199:14
|
||||
|
|
||||
LL | type A = _;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:199:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:201:14
|
||||
|
|
||||
LL | type B = _;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:201:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:203:14
|
||||
|
|
||||
LL | const C: _;
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/typeck_type_placeholder_item.rs:204:14
|
||||
--> $DIR/typeck_type_placeholder_item.rs:206:14
|
||||
|
|
||||
LL | const D: _ = 42;
|
||||
| ^
|
||||
|
|
@ -590,7 +608,7 @@ LL | const D: _ = 42;
|
|||
| not allowed in type signatures
|
||||
| help: replace `_` with the correct type: `i32`
|
||||
|
||||
error: aborting due to 64 previous errors
|
||||
error: aborting due to 66 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0121, E0282, E0403.
|
||||
For more information about an error, try `rustc --explain E0121`.
|
||||
|
|
|
|||
|
|
@ -48,18 +48,18 @@ error: union patterns should have exactly one field
|
|||
LL | let U { a, b } = u;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0026]: union `U` does not have a field named `c`
|
||||
--> $DIR/union-fields-2.rs:18:19
|
||||
|
|
||||
LL | let U { a, b, c } = u;
|
||||
| ^ union `U` does not have this field
|
||||
|
||||
error: union patterns should have exactly one field
|
||||
--> $DIR/union-fields-2.rs:18:9
|
||||
|
|
||||
LL | let U { a, b, c } = u;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error[E0026]: union `U` does not have a field named `c`
|
||||
--> $DIR/union-fields-2.rs:18:19
|
||||
|
|
||||
LL | let U { a, b, c } = u;
|
||||
| ^ union `U` does not have this field
|
||||
|
||||
error: union patterns should have exactly one field
|
||||
--> $DIR/union-fields-2.rs:20:9
|
||||
|
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ labels: L-lint
|
|||
|
||||
- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
|
||||
|
||||
*What benefit of this lint over old code?*
|
||||
*What is the advantage of the recommended code over the original code*
|
||||
|
||||
For example:
|
||||
- Remove bounce checking inserted by ...
|
||||
|
|
|
|||
|
|
@ -240,7 +240,8 @@ jobs:
|
|||
- 'Geal/nom'
|
||||
- 'rust-lang/stdarch'
|
||||
- 'serde-rs/serde'
|
||||
- 'chronotope/chrono'
|
||||
# FIXME: chrono currently cannot be compiled with `--all-targets`
|
||||
# - 'chronotope/chrono'
|
||||
- 'hyperium/hyper'
|
||||
- 'rust-random/rand'
|
||||
- 'rust-lang/futures-rs'
|
||||
|
|
|
|||
|
|
@ -1352,6 +1352,7 @@ Released 2018-09-13
|
|||
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
|
||||
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
|
||||
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
|
||||
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
|
||||
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
|
||||
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
|
||||
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
|
||||
|
|
@ -1508,9 +1509,11 @@ Released 2018-09-13
|
|||
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
|
||||
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
|
||||
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
|
||||
[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
|
||||
[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
|
||||
[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
|
||||
[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
|
||||
[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
|
||||
[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
|
||||
[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
|
||||
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
|
||||
|
|
@ -1575,6 +1578,7 @@ Released 2018-09-13
|
|||
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
|
||||
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
|
||||
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
|
||||
[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
|
||||
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
|
||||
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
|
||||
[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
|
||||
|
|
@ -1586,6 +1590,7 @@ Released 2018-09-13
|
|||
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
|
||||
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
|
||||
|
|
@ -1612,6 +1617,7 @@ Released 2018-09-13
|
|||
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
|
||||
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
|
||||
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
|
||||
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
|
||||
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
|
||||
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
|
||||
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ this to work, you will need the fix of `git subtree` available
|
|||
[here][gitgitgadget-pr].
|
||||
|
||||
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
|
||||
[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree
|
||||
[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
|
||||
[`rust-lang/rust`]: https://github.com/rust-lang/rust
|
||||
|
||||
## Issue and PR triage
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
use crate::reexport::Name;
|
||||
use crate::utils::{
|
||||
first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg,
|
||||
span_lint_and_then, without_block_comments,
|
||||
first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then, without_block_comments,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
|
|
@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||
use semver::Version;
|
||||
|
||||
static UNIX_SYSTEMS: &[&str] = &[
|
||||
|
|
@ -182,6 +182,29 @@ declare_clippy_lint! {
|
|||
"unknown_lints for scoped Clippy lints"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
|
||||
///
|
||||
/// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
|
||||
/// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #![deny(clippy::restriction)]
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// #![deny(clippy::as_conversions)]
|
||||
/// ```
|
||||
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
style,
|
||||
"enabling the complete restriction group"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
|
||||
/// with `#[rustfmt::skip]`.
|
||||
|
|
@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [
|
|||
DEPRECATED_SEMVER,
|
||||
USELESS_ATTRIBUTE,
|
||||
UNKNOWN_CLIPPY_LINTS,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
|
||||
if let Some(items) = &attr.meta_item_list() {
|
||||
if let Some(ident) = attr.ident() {
|
||||
match &*ident.as_str() {
|
||||
let ident = &*ident.as_str();
|
||||
match ident {
|
||||
"allow" | "warn" | "deny" | "forbid" => {
|
||||
check_clippy_lint_names(cx, items);
|
||||
check_clippy_lint_names(cx, ident, items);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
|
@ -363,38 +388,43 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match_else)]
|
||||
fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
|
||||
let lint_store = cx.lints();
|
||||
for lint in items {
|
||||
fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
|
||||
fn extract_name(lint: &NestedMetaItem) -> Option<SymbolStr> {
|
||||
if_chain! {
|
||||
if let Some(meta_item) = lint.meta_item();
|
||||
if meta_item.path.segments.len() > 1;
|
||||
if let tool_name = meta_item.path.segments[0].ident;
|
||||
if tool_name.as_str() == "clippy";
|
||||
let name = meta_item.path.segments.last().unwrap().ident.name;
|
||||
if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
|
||||
&name.as_str(),
|
||||
Some(tool_name.name),
|
||||
);
|
||||
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
|
||||
then {
|
||||
return Some(lint_name.as_str());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
let lint_store = cx.lints();
|
||||
for lint in items {
|
||||
if let Some(lint_name) = extract_name(lint) {
|
||||
if let CheckLintNameResult::Tool(Err((None, _))) =
|
||||
lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNKNOWN_CLIPPY_LINTS,
|
||||
lint.span(),
|
||||
&format!("unknown clippy lint: clippy::{}", name),
|
||||
&format!("unknown clippy lint: clippy::{}", lint_name),
|
||||
|diag| {
|
||||
let name_lower = name.as_str().to_lowercase();
|
||||
let symbols = lint_store.get_lints().iter().map(
|
||||
|l| Symbol::intern(&l.name_lower())
|
||||
).collect::<Vec<_>>();
|
||||
let sugg = find_best_match_for_name(
|
||||
symbols.iter(),
|
||||
&format!("clippy::{}", name_lower),
|
||||
None,
|
||||
);
|
||||
if name.as_str().chars().any(char::is_uppercase)
|
||||
&& lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
|
||||
let name_lower = lint_name.to_lowercase();
|
||||
let symbols = lint_store
|
||||
.get_lints()
|
||||
.iter()
|
||||
.map(|l| Symbol::intern(&l.name_lower()))
|
||||
.collect::<Vec<_>>();
|
||||
let sugg = find_best_match_for_name(symbols.iter(), &format!("clippy::{}", name_lower), None);
|
||||
if lint_name.chars().any(char::is_uppercase)
|
||||
&& lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok()
|
||||
{
|
||||
diag.span_suggestion(
|
||||
lint.span(),
|
||||
"lowercase the lint name",
|
||||
|
|
@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if lint_name == "restriction" && ident != "allow" {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
lint.span(),
|
||||
"restriction lints are not meant to be all enabled",
|
||||
None,
|
||||
"try enabling only the lints you really need",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -442,15 +481,14 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
|
||||
if let Some(stmt) = block.stmts.first() {
|
||||
match &stmt.kind {
|
||||
block.stmts.first().map_or(
|
||||
block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)),
|
||||
|stmt| match &stmt.kind {
|
||||
StmtKind::Local(_) => true,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
|
||||
|
|
@ -460,11 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &
|
|||
ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
|
||||
ExprKind::Call(path_expr, _) => {
|
||||
if let ExprKind::Path(qpath) = &path_expr.kind {
|
||||
if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
|
||||
!match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
tables
|
||||
.qpath_res(qpath, path_expr.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ declare_clippy_lint! {
|
|||
/// non-async-aware MutexGuard.
|
||||
///
|
||||
/// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
|
||||
/// are not designed to operator in an async context across await points.
|
||||
/// are not designed to operate in an async context across await points.
|
||||
///
|
||||
/// There are two potential solutions. One is to use an asynx-aware Mutex
|
||||
/// type. Many asynchronous foundation crates provide such a Mutex type. The
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
|
|||
COLLAPSIBLE_IF,
|
||||
block.span,
|
||||
"this `else { if .. }` block can be collapsed",
|
||||
"try",
|
||||
"collapse nested if block",
|
||||
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
|
||||
applicability,
|
||||
);
|
||||
|
|
@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
|||
let rhs = Sugg::ast(cx, check_inner, "..");
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try",
|
||||
"collapse nested if block",
|
||||
format!(
|
||||
"if {} {}",
|
||||
lhs.and(&rhs),
|
||||
|
|
|
|||
|
|
@ -122,8 +122,5 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
|
|||
}
|
||||
|
||||
fn kind_is_cmp(kind: BinOpKind) -> bool {
|
||||
match kind {
|
||||
BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,5 +153,13 @@ declare_deprecated_lint! {
|
|||
///
|
||||
/// **Deprecation reason:** Associated-constants are now preferred.
|
||||
pub REPLACE_CONSTS,
|
||||
"associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants"
|
||||
"associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** The regex! macro does not exist anymore.
|
||||
pub REGEX_MACRO,
|
||||
"the regex! macro has been removed from the regex crate in 2018"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,9 +73,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
|
||||
match method_name {
|
||||
"deref" => {
|
||||
if cx.tcx.lang_items().deref_trait().map_or(false, |id| {
|
||||
let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
|
||||
implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
|
||||
}) {
|
||||
});
|
||||
if impls_deref_trait {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EXPLICIT_DEREF_METHODS,
|
||||
|
|
@ -88,9 +89,10 @@ fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var
|
|||
}
|
||||
},
|
||||
"deref_mut" => {
|
||||
if cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
|
||||
let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
|
||||
implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
|
||||
}) {
|
||||
});
|
||||
if impls_deref_mut_trait {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EXPLICIT_DEREF_METHODS,
|
||||
|
|
|
|||
|
|
@ -214,20 +214,20 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
|
|||
}
|
||||
|
||||
fn is_valid_operator(op: BinOp) -> bool {
|
||||
match op.node {
|
||||
matches!(
|
||||
op.node,
|
||||
BinOpKind::Sub
|
||||
| BinOpKind::Div
|
||||
| BinOpKind::Eq
|
||||
| BinOpKind::Lt
|
||||
| BinOpKind::Le
|
||||
| BinOpKind::Gt
|
||||
| BinOpKind::Ge
|
||||
| BinOpKind::Ne
|
||||
| BinOpKind::And
|
||||
| BinOpKind::Or
|
||||
| BinOpKind::BitXor
|
||||
| BinOpKind::BitAnd
|
||||
| BinOpKind::BitOr => true,
|
||||
_ => false,
|
||||
}
|
||||
| BinOpKind::Div
|
||||
| BinOpKind::Eq
|
||||
| BinOpKind::Lt
|
||||
| BinOpKind::Le
|
||||
| BinOpKind::Gt
|
||||
| BinOpKind::Ge
|
||||
| BinOpKind::Ne
|
||||
| BinOpKind::And
|
||||
| BinOpKind::Or
|
||||
| BinOpKind::BitXor
|
||||
| BinOpKind::BitAnd
|
||||
| BinOpKind::BitOr
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,10 +105,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
|
|||
_ => return false,
|
||||
}
|
||||
|
||||
match map.find(map.get_parent_node(id)) {
|
||||
Some(Node::Param(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -175,10 +175,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a
|
|||
fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
|
||||
match (&lhs.kind, &rhs.kind) {
|
||||
(ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
|
||||
(l, r) => match (l, r) {
|
||||
(ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
|
||||
(_, _) => true,
|
||||
},
|
||||
(l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::consts::{
|
||||
constant, constant_simple, Constant,
|
||||
Constant::{F32, F64},
|
||||
Constant::{Int, F32, F64},
|
||||
};
|
||||
use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
|
||||
use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) {
|
||||
if value == Int(2) {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind {
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
ref lhs,
|
||||
ref rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"square can be computed more efficiently",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, &args[0], ".."),
|
||||
Sugg::hir(cx, &args[0], ".."),
|
||||
Sugg::hir(cx, &other_addend, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"square can be computed more efficiently",
|
||||
"consider using",
|
||||
format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
ref add_lhs,
|
||||
ref add_rhs,
|
||||
) = args[0].kind
|
||||
{
|
||||
// check if expression of the form x * x + y * y
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
|
||||
if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
|
||||
if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
|
||||
then {
|
||||
return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
|
||||
}
|
||||
}
|
||||
|
||||
// check if expression of the form x.powi(2) + y.powi(2)
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(
|
||||
PathSegment { ident: lmethod_name, .. },
|
||||
ref _lspan,
|
||||
ref largs,
|
||||
_
|
||||
) = add_lhs.kind;
|
||||
if let ExprKind::MethodCall(
|
||||
PathSegment { ident: rmethod_name, .. },
|
||||
ref _rspan,
|
||||
ref rargs,
|
||||
_
|
||||
) = add_rhs.kind;
|
||||
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
|
||||
if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]);
|
||||
if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]);
|
||||
if Int(2) == lvalue && Int(2) == rvalue;
|
||||
then {
|
||||
return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(message) = detect_hypot(cx, args) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"hypotenuse can be computed more accurately",
|
||||
"consider using",
|
||||
message,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
|
||||
// and suggest usage of `x.exp_m1() - (y - 1)` instead
|
||||
fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
|
|
@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
rhs,
|
||||
) = &expr.kind
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
|
||||
(inner_lhs, inner_rhs, rhs)
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
|
||||
|
|
@ -479,6 +602,100 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind;
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind;
|
||||
then {
|
||||
return method_name_a.as_str() == method_name_b.as_str() &&
|
||||
args_a.len() == args_b.len() &&
|
||||
(
|
||||
["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
|
||||
method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
// check if expression of the form x.logN() / y.logN()
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind;
|
||||
if are_same_base_logs(cx, lhs, rhs);
|
||||
if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind;
|
||||
if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"log base can be expressed more clearly",
|
||||
"consider using",
|
||||
format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
div_lhs,
|
||||
div_rhs,
|
||||
) = &expr.kind;
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
mul_lhs,
|
||||
mul_rhs,
|
||||
) = &div_lhs.kind;
|
||||
if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs);
|
||||
if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs);
|
||||
then {
|
||||
// TODO: also check for constant values near PI/180 or 180/PI
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
|
||||
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to degrees can be done more accurately",
|
||||
"consider using",
|
||||
format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if
|
||||
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
|
||||
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to radians can be done more accurately",
|
||||
"consider using",
|
||||
format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind {
|
||||
|
|
@ -489,6 +706,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
|||
"ln" => check_ln1p(cx, expr, args),
|
||||
"log" => check_log_base(cx, expr, args),
|
||||
"powf" => check_powf(cx, expr, args),
|
||||
"powi" => check_powi(cx, expr, args),
|
||||
"sqrt" => check_hypot(cx, expr, args),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -496,6 +715,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
|||
check_expm1(cx, expr);
|
||||
check_mul_add(cx, expr);
|
||||
check_custom_abs(cx, expr);
|
||||
check_log_division(cx, expr);
|
||||
check_radians(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,18 +305,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
|
|||
}
|
||||
|
||||
fn is_block(expr: &Expr) -> bool {
|
||||
if let ExprKind::Block(..) = expr.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(expr.kind, ExprKind::Block(..))
|
||||
}
|
||||
|
||||
/// Check if the expression is an `if` or `if let`
|
||||
fn is_if(expr: &Expr) -> bool {
|
||||
if let ExprKind::If(..) = expr.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(expr.kind, ExprKind::If(..))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -645,13 +645,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
|
|||
use hir::ExprKind::{Field, Index, Path};
|
||||
|
||||
match e.kind {
|
||||
Path(ref qpath) => {
|
||||
if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)),
|
||||
Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,13 +135,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ArmVisitor<'_, 'tcx> {
|
||||
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
|
||||
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
|
||||
if let Some(arm_mutex) = self.found_mutex {
|
||||
SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
self.found_mutex
|
||||
.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,16 +302,12 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
|
||||
let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr));
|
||||
match ty.kind {
|
||||
ty::Dynamic(ref tt, ..) => {
|
||||
if let Some(principal) = tt.principal() {
|
||||
cx.tcx
|
||||
.associated_items(principal.def_id())
|
||||
.in_definition_order()
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
|
||||
cx.tcx
|
||||
.associated_items(principal.def_id())
|
||||
.in_definition_order()
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
}),
|
||||
ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
|
||||
ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
|
||||
ty::Array(..) | ty::Slice(..) | ty::Str => true,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
|
||||
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `let`-bindings, which are subsequently
|
||||
|
|
@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> {
|
|||
borrows: bool,
|
||||
}
|
||||
|
||||
impl BorrowVisitor<'_, '_> {
|
||||
fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id),
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(qpath),
|
||||
..
|
||||
},
|
||||
..,
|
||||
) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
|
|
@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(def_id) = self.fn_def_id(expr) {
|
||||
if let Some(def_id) = fn_def_id(self.cx, expr) {
|
||||
self.borrows = self
|
||||
.cx
|
||||
.tcx
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ mod main_recursion;
|
|||
mod manual_async_fn;
|
||||
mod manual_non_exhaustive;
|
||||
mod map_clone;
|
||||
mod map_identity;
|
||||
mod map_unit_fn;
|
||||
mod match_on_vec_items;
|
||||
mod matches;
|
||||
|
|
@ -263,10 +264,12 @@ mod non_copy_const;
|
|||
mod non_expressive_names;
|
||||
mod open_options;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
mod overflow_check_conditional;
|
||||
mod panic_unimplemented;
|
||||
mod partialeq_ne_impl;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_offset_with_cast;
|
||||
|
|
@ -274,11 +277,11 @@ mod question_mark;
|
|||
mod ranges;
|
||||
mod redundant_clone;
|
||||
mod redundant_field_names;
|
||||
mod redundant_pattern_matching;
|
||||
mod redundant_pub_crate;
|
||||
mod redundant_static_lifetimes;
|
||||
mod reference;
|
||||
mod regex;
|
||||
mod repeat_once;
|
||||
mod returns;
|
||||
mod serde_api;
|
||||
mod shadow;
|
||||
|
|
@ -459,7 +462,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
);
|
||||
store.register_removed(
|
||||
"clippy::replace_consts",
|
||||
"associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants",
|
||||
"associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::regex_macro",
|
||||
"the regex! macro has been removed from the regex crate in 2018",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
|
|
@ -473,6 +480,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&assign_ops::ASSIGN_OP_PATTERN,
|
||||
&assign_ops::MISREFACTORED_ASSIGN_OP,
|
||||
&atomic_ordering::INVALID_ATOMIC_ORDERING,
|
||||
&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
&attrs::DEPRECATED_CFG_ATTR,
|
||||
&attrs::DEPRECATED_SEMVER,
|
||||
&attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
|
|
@ -608,17 +616,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&manual_async_fn::MANUAL_ASYNC_FN,
|
||||
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
&map_clone::MAP_CLONE,
|
||||
&map_identity::MAP_IDENTITY,
|
||||
&map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
&map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
&match_on_vec_items::MATCH_ON_VEC_ITEMS,
|
||||
&matches::INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
&matches::MATCH_AS_REF,
|
||||
&matches::MATCH_BOOL,
|
||||
&matches::MATCH_LIKE_MATCHES_MACRO,
|
||||
&matches::MATCH_OVERLAPPING_ARM,
|
||||
&matches::MATCH_REF_PATS,
|
||||
&matches::MATCH_SINGLE_BINDING,
|
||||
&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
|
||||
&matches::MATCH_WILD_ERR_ARM,
|
||||
&matches::REDUNDANT_PATTERN_MATCHING,
|
||||
&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
|
||||
&matches::SINGLE_MATCH,
|
||||
&matches::SINGLE_MATCH_ELSE,
|
||||
|
|
@ -726,6 +737,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&non_expressive_names::SIMILAR_NAMES,
|
||||
&open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
&option_env_unwrap::OPTION_ENV_UNWRAP,
|
||||
&option_if_let_else::OPTION_IF_LET_ELSE,
|
||||
&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
|
||||
&panic_unimplemented::PANIC,
|
||||
&panic_unimplemented::PANIC_PARAMS,
|
||||
|
|
@ -734,6 +746,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&panic_unimplemented::UNREACHABLE,
|
||||
&partialeq_ne_impl::PARTIALEQ_NE_IMPL,
|
||||
&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
&pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
&precedence::PRECEDENCE,
|
||||
&ptr::CMP_NULL,
|
||||
&ptr::MUT_FROM_REF,
|
||||
|
|
@ -746,14 +759,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&ranges::REVERSED_EMPTY_RANGES,
|
||||
&redundant_clone::REDUNDANT_CLONE,
|
||||
&redundant_field_names::REDUNDANT_FIELD_NAMES,
|
||||
&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
|
||||
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
|
||||
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
|
||||
&reference::DEREF_ADDROF,
|
||||
&reference::REF_IN_DEREF,
|
||||
®ex::INVALID_REGEX,
|
||||
®ex::REGEX_MACRO,
|
||||
®ex::TRIVIAL_REGEX,
|
||||
&repeat_once::REPEAT_ONCE,
|
||||
&returns::NEEDLESS_RETURN,
|
||||
&returns::UNUSED_UNIT,
|
||||
&serde_api::SERDE_API_MISUSE,
|
||||
|
|
@ -946,7 +958,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box missing_doc::MissingDoc::new());
|
||||
store.register_late_pass(|| box missing_inline::MissingInline);
|
||||
store.register_late_pass(|| box if_let_some_result::OkIfLet);
|
||||
store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
|
||||
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
|
||||
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
|
||||
let enum_variant_size_threshold = conf.enum_variant_size_threshold;
|
||||
|
|
@ -990,7 +1001,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box checked_conversions::CheckedConversions);
|
||||
store.register_late_pass(|| box integer_division::IntegerDivision);
|
||||
store.register_late_pass(|| box inherent_to_string::InherentToString);
|
||||
store.register_late_pass(|| box trait_bounds::TraitBounds);
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds));
|
||||
store.register_late_pass(|| box comparison_chain::ComparisonChain);
|
||||
store.register_late_pass(|| box mut_key::MutableKeyType);
|
||||
store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
|
||||
|
|
@ -1027,7 +1039,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
let array_size_threshold = conf.array_size_threshold;
|
||||
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
|
||||
store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
|
||||
store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
|
||||
store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
|
||||
store.register_early_pass(|| box as_conversions::AsConversions);
|
||||
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
|
||||
store.register_late_pass(|| box let_underscore::LetUnderscore);
|
||||
|
|
@ -1043,6 +1055,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
|
||||
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
|
||||
store.register_late_pass(|| box dereference::Dereferencing);
|
||||
store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
|
||||
store.register_late_pass(|| box future_not_send::FutureNotSend);
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
|
||||
|
|
@ -1057,6 +1070,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
});
|
||||
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
|
||||
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
||||
store.register_late_pass(|| box map_identity::MapIdentity);
|
||||
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
||||
store.register_late_pass(|| box repeat_once::RepeatOnce);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
|
@ -1090,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&panic_unimplemented::TODO),
|
||||
LintId::of(&panic_unimplemented::UNIMPLEMENTED),
|
||||
LintId::of(&panic_unimplemented::UNREACHABLE),
|
||||
LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
|
||||
LintId::of(&shadow::SHADOW_REUSE),
|
||||
LintId::of(&shadow::SHADOW_SAME),
|
||||
LintId::of(&strings::STRING_ADD),
|
||||
|
|
@ -1146,6 +1163,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&needless_continue::NEEDLESS_CONTINUE),
|
||||
LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
|
||||
LintId::of(&non_expressive_names::SIMILAR_NAMES),
|
||||
LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_PLUS_ONE),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
|
|
@ -1186,6 +1205,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
|
||||
LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
|
||||
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(&attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(&attrs::DEPRECATED_SEMVER),
|
||||
LintId::of(&attrs::MISMATCHED_TARGET_OS),
|
||||
|
|
@ -1273,13 +1293,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(&map_clone::MAP_CLONE),
|
||||
LintId::of(&map_identity::MAP_IDENTITY),
|
||||
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(&matches::MATCH_AS_REF),
|
||||
LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
|
||||
LintId::of(&matches::MATCH_OVERLAPPING_ARM),
|
||||
LintId::of(&matches::MATCH_REF_PATS),
|
||||
LintId::of(&matches::MATCH_SINGLE_BINDING),
|
||||
LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(&matches::SINGLE_MATCH),
|
||||
LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
|
||||
|
|
@ -1364,18 +1387,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&ptr::PTR_ARG),
|
||||
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(&question_mark::QUESTION_MARK),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(&ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(&redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
LintId::of(®ex::INVALID_REGEX),
|
||||
LintId::of(®ex::REGEX_MACRO),
|
||||
LintId::of(®ex::TRIVIAL_REGEX),
|
||||
LintId::of(&repeat_once::REPEAT_ONCE),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&returns::UNUSED_UNIT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
|
|
@ -1437,6 +1458,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
|
||||
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
|
||||
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
|
||||
|
|
@ -1470,8 +1492,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(&map_clone::MAP_CLONE),
|
||||
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
|
||||
LintId::of(&matches::MATCH_OVERLAPPING_ARM),
|
||||
LintId::of(&matches::MATCH_REF_PATS),
|
||||
LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(&matches::SINGLE_MATCH),
|
||||
LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
|
||||
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
|
|
@ -1508,9 +1532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&ptr::PTR_ARG),
|
||||
LintId::of(&question_mark::QUESTION_MARK),
|
||||
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(®ex::REGEX_MACRO),
|
||||
LintId::of(®ex::TRIVIAL_REGEX),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&returns::UNUSED_UNIT),
|
||||
|
|
@ -1550,6 +1572,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
|
||||
LintId::of(&loops::MUT_RANGE_BOUND),
|
||||
LintId::of(&loops::WHILE_LET_LOOP),
|
||||
LintId::of(&map_identity::MAP_IDENTITY),
|
||||
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(&matches::MATCH_AS_REF),
|
||||
|
|
@ -1580,10 +1603,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(&precedence::PRECEDENCE),
|
||||
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
LintId::of(&repeat_once::REPEAT_ONCE),
|
||||
LintId::of(&swap::MANUAL_SWAP),
|
||||
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
|
||||
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
|
||||
|
|
|
|||
|
|
@ -129,10 +129,10 @@ fn check_fn_inner<'tcx>(
|
|||
}
|
||||
|
||||
let mut bounds_lts = Vec::new();
|
||||
let types = generics.params.iter().filter(|param| match param.kind {
|
||||
GenericParamKind::Type { .. } => true,
|
||||
_ => false,
|
||||
});
|
||||
let types = generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
|
||||
for typ in types {
|
||||
for bound in typ.bounds {
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
|
|
@ -337,10 +337,10 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
|
|||
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
|
||||
if let Some(ref last_path_segment) = last_path_segment(qpath).args {
|
||||
if !last_path_segment.parenthesized
|
||||
&& !last_path_segment.args.iter().any(|arg| match arg {
|
||||
GenericArg::Lifetime(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
&& !last_path_segment
|
||||
.args
|
||||
.iter()
|
||||
.any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
{
|
||||
let hir_id = ty.hir_id;
|
||||
match self.cx.qpath_res(qpath, hir_id) {
|
||||
|
|
|
|||
|
|
@ -264,10 +264,13 @@ impl LiteralDigitGrouping {
|
|||
|
||||
let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
|
||||
(exponent, &["32", "64"][..], 'f')
|
||||
} else if let Some(fraction) = &mut num_lit.fraction {
|
||||
(fraction, &["32", "64"][..], 'f')
|
||||
} else {
|
||||
(&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
|
||||
num_lit
|
||||
.fraction
|
||||
.as_mut()
|
||||
.map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| {
|
||||
(fraction, &["32", "64"][..], 'f')
|
||||
})
|
||||
};
|
||||
|
||||
let mut split = part.rsplit('_');
|
||||
|
|
|
|||
|
|
@ -686,13 +686,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
|||
NeverLoopResult::AlwaysBreak
|
||||
}
|
||||
},
|
||||
ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
|
||||
if let Some(ref e) = *e {
|
||||
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
|
||||
} else {
|
||||
NeverLoopResult::AlwaysBreak
|
||||
}
|
||||
},
|
||||
ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
|
||||
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
|
||||
}),
|
||||
ExprKind::InlineAsm(ref asm) => asm
|
||||
.operands
|
||||
.iter()
|
||||
|
|
@ -1881,13 +1877,9 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
|||
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||
match ty.kind {
|
||||
ty::Array(_, n) => {
|
||||
if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) {
|
||||
(0..=32).contains(&val)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
ty::Array(_, n) => n
|
||||
.try_eval_usize(cx.tcx, cx.param_env)
|
||||
.map_or(false, |val| (0..=32).contains(&val)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1899,11 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<
|
|||
return None;
|
||||
}
|
||||
if let StmtKind::Local(ref local) = block.stmts[0].kind {
|
||||
if let Some(expr) = local.init {
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
local.init //.map(|expr| expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -2023,15 +2011,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
|||
if let PatKind::Binding(.., ident, _) = local.pat.kind {
|
||||
self.name = Some(ident.name);
|
||||
|
||||
self.state = if let Some(ref init) = local.init {
|
||||
self.state = local.init.as_ref().map_or(VarState::Declared, |init| {
|
||||
if is_integer_const(&self.cx, init, 0) {
|
||||
VarState::Warn
|
||||
} else {
|
||||
VarState::Declared
|
||||
}
|
||||
} else {
|
||||
VarState::Declared
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2105,17 +2091,11 @@ fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<HirId> {
|
|||
}
|
||||
|
||||
fn is_loop(expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Loop(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(expr.kind, ExprKind::Loop(..))
|
||||
}
|
||||
|
||||
fn is_conditional(expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Match(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(expr.kind, ExprKind::Match(..))
|
||||
}
|
||||
|
||||
fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
|
||||
|
|
|
|||
126
src/tools/clippy/clippy_lints/src/map_identity.rs
Normal file
126
src/tools/clippy/clippy_lints/src/map_identity.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use crate::utils::{
|
||||
is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
|
||||
span_lint_and_sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
|
||||
///
|
||||
/// **Why is this bad?** It can be written more concisely without the call to `map`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = [1, 2, 3];
|
||||
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = [1, 2, 3];
|
||||
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
|
||||
/// ```
|
||||
pub MAP_IDENTITY,
|
||||
complexity,
|
||||
"using iterator.map(|x| x)"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MapIdentity {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some([caller, func]) = get_map_argument(cx, expr);
|
||||
if is_expr_identity_function(cx, func);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_IDENTITY,
|
||||
expr.span.trim_start(caller.span).unwrap(),
|
||||
"unnecessary map of the identity function",
|
||||
"remove the call to `map`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the arguments passed into map() if the expression is a method call to
|
||||
/// map(). Otherwise, returns None.
|
||||
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
|
||||
if args.len() == 2 && method.ident.as_str() == "map";
|
||||
let caller_ty = cx.tables().expr_ty(&args[0]);
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR)
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
|
||||
then {
|
||||
Some(args)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an expression represents the identity function
|
||||
/// Only examines closures and `std::convert::identity`
|
||||
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||
ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a function's body represents the identity function
|
||||
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
|
||||
/// return x; }`
|
||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||
let params = func.params;
|
||||
let body = remove_blocks(&func.value);
|
||||
|
||||
// if there's less/more than one parameter, then it is not the identity function
|
||||
if params.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
match body.kind {
|
||||
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
|
||||
ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
|
||||
ExprKind::Block(ref block, _) => {
|
||||
if_chain! {
|
||||
if block.stmts.len() == 1;
|
||||
if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind;
|
||||
if let ExprKind::Ret(Some(ref ret_val)) = expr.kind;
|
||||
then {
|
||||
match_expr_param(cx, ret_val, params[0].pat)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff an expression returns the same thing as a parameter's pattern
|
||||
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
||||
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
match_var(expr, ident.name) && !(cx.tables().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::{
|
||||
Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
|
||||
QPath, RangeEnd,
|
||||
Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat,
|
||||
PatKind, QPath, RangeEnd,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::Bound;
|
||||
|
||||
|
|
@ -409,6 +409,74 @@ declare_clippy_lint! {
|
|||
"a match on a struct that binds all fields but still uses the wildcard pattern"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result` or
|
||||
/// `Option`
|
||||
///
|
||||
/// **Why is this bad?** It's more concise and clear to just use the proper
|
||||
/// utility function
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
/// if let Err(_) = Err::<i32, i32>(42) {}
|
||||
/// if let None = None::<()> {}
|
||||
/// if let Some(_) = Some(42) {}
|
||||
/// match Ok::<i32, i32>(42) {
|
||||
/// Ok(_) => true,
|
||||
/// Err(_) => false,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The more idiomatic use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// if Ok::<i32, i32>(42).is_ok() {}
|
||||
/// if Err::<i32, i32>(42).is_err() {}
|
||||
/// if None::<()>.is_none() {}
|
||||
/// if Some(42).is_some() {}
|
||||
/// Ok::<i32, i32>(42).is_ok();
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN_MATCHING,
|
||||
style,
|
||||
"use the proper utility function avoiding an `if let`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `match` or `if let` expressions producing a
|
||||
/// `bool` that could be written using `matches!`
|
||||
///
|
||||
/// **Why is this bad?** Readability and needless complexity.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Some(5);
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = match x {
|
||||
/// Some(0) => true,
|
||||
/// _ => false,
|
||||
/// };
|
||||
///
|
||||
/// let a = if let Some(0) = x {
|
||||
/// true
|
||||
/// } else {
|
||||
/// false
|
||||
/// };
|
||||
///
|
||||
/// // Good
|
||||
/// let a = matches!(x, Some(0));
|
||||
/// ```
|
||||
pub MATCH_LIKE_MATCHES_MACRO,
|
||||
style,
|
||||
"a match that could be written with the matches! macro"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
infallible_destructuring_match_linted: bool,
|
||||
|
|
@ -427,7 +495,9 @@ impl_lint_pass!(Matches => [
|
|||
WILDCARD_IN_OR_PATTERNS,
|
||||
MATCH_SINGLE_BINDING,
|
||||
INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
REST_PAT_IN_FULLY_BOUND_STRUCTS
|
||||
REST_PAT_IN_FULLY_BOUND_STRUCTS,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
MATCH_LIKE_MATCHES_MACRO
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
|
|
@ -435,6 +505,10 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
redundant_pattern_match::check(cx, expr);
|
||||
check_match_like_matches(cx, expr);
|
||||
|
||||
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
|
||||
check_single_match(cx, ex, arms, expr);
|
||||
check_match_bool(cx, ex, arms, expr);
|
||||
|
|
@ -530,16 +604,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
|
|||
// the lint noisy in unnecessary situations
|
||||
return;
|
||||
}
|
||||
let els = remove_blocks(&arms[1].body);
|
||||
let els = if is_unit_expr(els) {
|
||||
let els = arms[1].body;
|
||||
let els = if is_unit_expr(remove_blocks(els)) {
|
||||
None
|
||||
} else if let ExprKind::Block(_, _) = els.kind {
|
||||
// matches with blocks that contain statements are prettier as `if let + else`
|
||||
Some(els)
|
||||
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
|
||||
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
|
||||
// single statement/expr "else" block, don't lint
|
||||
return;
|
||||
} else {
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
}
|
||||
} else {
|
||||
// allow match arms with just expressions
|
||||
return;
|
||||
// not a block, don't lint
|
||||
return;
|
||||
};
|
||||
|
||||
let ty = cx.tables().expr_ty(ex);
|
||||
if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
|
||||
check_single_match_single_pattern(cx, ex, arms, expr, els);
|
||||
|
|
@ -802,13 +882,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
// Some simple checks for exhaustive patterns.
|
||||
// There is a room for improvements to detect more cases,
|
||||
// but it can be more expensive to do so.
|
||||
let is_pattern_exhaustive = |pat: &&Pat<'_>| {
|
||||
if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
let is_pattern_exhaustive =
|
||||
|pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
|
||||
if patterns.iter().all(is_pattern_exhaustive) {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
|
|
@ -989,6 +1064,79 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
||||
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
|
||||
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lint a `match` or desugared `if let` for replacement by `matches!`
|
||||
fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if cx.tables().expr_ty(expr).is_bool();
|
||||
if is_wild(&arms[1].pat);
|
||||
if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
|
||||
if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
|
||||
if first != second;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
|
||||
format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
|
||||
} else {
|
||||
format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
expr.span,
|
||||
&format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
|
||||
"try this",
|
||||
format!(
|
||||
"{}matches!({}, {})",
|
||||
if first { "" } else { "!" },
|
||||
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
|
||||
pat_and_guard,
|
||||
),
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a `bool` or `{ bool }`
|
||||
fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
|
||||
match ex {
|
||||
ExprKind::Lit(Spanned {
|
||||
node: LitKind::Bool(b), ..
|
||||
}) => Some(*b),
|
||||
ExprKind::Block(
|
||||
rustc_hir::Block {
|
||||
stmts: &[],
|
||||
expr: Some(exp),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) if desugared => {
|
||||
if let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Bool(b), ..
|
||||
}) = exp.kind
|
||||
{
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
|
||||
return;
|
||||
|
|
@ -1179,10 +1327,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
|
|||
|
||||
// Checks if arm has the form `None => None`
|
||||
fn is_none_arm(arm: &Arm<'_>) -> bool {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
|
||||
}
|
||||
|
||||
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
|
||||
|
|
@ -1293,6 +1438,229 @@ where
|
|||
None
|
||||
}
|
||||
|
||||
mod redundant_pattern_match {
|
||||
use super::REDUNDANT_PATTERN_MATCHING;
|
||||
use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_mir::const_eval::is_const_fn;
|
||||
use rustc_span::source_map::Symbol;
|
||||
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
|
||||
MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
|
||||
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_sugg_for_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
keyword: &'static str,
|
||||
) {
|
||||
fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
|
||||
if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
|
||||
return Some("is_ok()");
|
||||
}
|
||||
if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
|
||||
return Some("is_err()");
|
||||
}
|
||||
if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
|
||||
return Some("is_some()");
|
||||
}
|
||||
if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
|
||||
return Some("is_none()");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
let hir_id = expr.hir_id;
|
||||
let good_method = match arms[0].pat.kind {
|
||||
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_suggestion(cx, hir_id, path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
|
||||
_ => None,
|
||||
};
|
||||
let good_method = match good_method {
|
||||
Some(method) => method,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
|
||||
if method_path.ident.name == sym!(next);
|
||||
if match_trait_method(cx, op, &paths::ITERATOR);
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pat.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
// while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let expr_span = expr.span;
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// ^^^
|
||||
let op_span = op.span.source_callsite();
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(op_span.shrink_to_hi());
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||
if arms.len() == 2 {
|
||||
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
|
||||
|
||||
let hir_id = expr.hir_id;
|
||||
let found_good_method = match node_pair {
|
||||
(
|
||||
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
|
||||
PatKind::TupleStruct(ref path_right, ref patterns_right, _),
|
||||
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
|
||||
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::RESULT_OK,
|
||||
&paths::RESULT_ERR,
|
||||
"is_ok()",
|
||||
"is_err()",
|
||||
|| can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
|
||||
|| can_suggest(cx, hir_id, sym!(result_type), "is_err"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
(PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
|
||||
| (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
|
||||
if patterns.len() == 1 =>
|
||||
{
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::OPTION_SOME,
|
||||
&paths::OPTION_NONE,
|
||||
"is_some()",
|
||||
"is_none()",
|
||||
|| can_suggest(cx, hir_id, sym!(option_type), "is_some"),
|
||||
|| can_suggest(cx, hir_id, sym!(option_type), "is_none"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(good_method) = found_good_method {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
expr.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
let span = expr.span.to(op.span);
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("{}.{}", snippet(cx, op.span, "_"), good_method),
|
||||
Applicability::MaybeIncorrect, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn find_good_method_for_match<'a>(
|
||||
arms: &[Arm<'_>],
|
||||
path_left: &QPath<'_>,
|
||||
path_right: &QPath<'_>,
|
||||
expected_left: &[&str],
|
||||
expected_right: &[&str],
|
||||
should_be_left: &'a str,
|
||||
should_be_right: &'a str,
|
||||
can_suggest_left: impl Fn() -> bool,
|
||||
can_suggest_right: impl Fn() -> bool,
|
||||
) -> Option<&'a str> {
|
||||
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
|
||||
(&(*arms[0].body).kind, &(*arms[1].body).kind)
|
||||
} else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
|
||||
(&(*arms[1].body).kind, &(*arms[0].body).kind)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match body_node_pair {
|
||||
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||
(LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
|
||||
(LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
|
||||
if !in_constant(cx, hir_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
|
||||
cx.tcx
|
||||
.get_diagnostic_item(diag_item)
|
||||
.and_then(|def_id| {
|
||||
cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
|
||||
cx.tcx
|
||||
.associated_items(*imp)
|
||||
.in_definition_order()
|
||||
.find_map(|item| match item.kind {
|
||||
ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overlapping() {
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
|
|
|
|||
|
|
@ -1844,10 +1844,10 @@ fn lint_expect_fun_call(
|
|||
ty::Ref(ty::ReStatic, ..)
|
||||
)
|
||||
}),
|
||||
hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) {
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true,
|
||||
_ => false,
|
||||
},
|
||||
hir::ExprKind::Path(ref p) => matches!(
|
||||
cx.qpath_res(p, arg.hir_id),
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -2028,13 +2028,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
|||
.tables()
|
||||
.expr_adjustments(arg)
|
||||
.iter()
|
||||
.filter(|adj| {
|
||||
if let ty::adjustment::Adjust::Deref(_) = adj.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
|
||||
.count();
|
||||
let derefs: String = iter::repeat('*').take(deref_count).collect();
|
||||
snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
|
||||
|
|
@ -2044,7 +2038,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
|||
}
|
||||
span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified);
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -2460,13 +2454,9 @@ fn derefs_to_slice<'tcx>(
|
|||
ty::Slice(_) => true,
|
||||
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
|
||||
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
|
||||
ty::Array(_, size) => {
|
||||
if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) {
|
||||
size < 32
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
ty::Array(_, size) => size
|
||||
.try_eval_usize(cx.tcx, cx.param_env)
|
||||
.map_or(false, |size| size < 32),
|
||||
ty::Ref(_, inner, _) => may_slice(cx, inner),
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,13 +77,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
|||
}
|
||||
(true, true)
|
||||
},
|
||||
hir::ExprKind::Block(ref block, _) => {
|
||||
if let Some(expr) = &block.expr {
|
||||
check_expression(cx, arg_id, &expr)
|
||||
} else {
|
||||
(false, false)
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Block(ref block, _) => block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or((false, false), |expr| check_expression(cx, arg_id, &expr)),
|
||||
hir::ExprKind::Match(_, arms, _) => {
|
||||
let mut found_mapping = false;
|
||||
let mut found_filtering = false;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
enum MinMax {
|
||||
Min,
|
||||
Max,
|
||||
|
|
@ -86,16 +86,15 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt
|
|||
if args.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
if let Some(c) = constant_simple(cx, cx.tables(), &args[0]) {
|
||||
if constant_simple(cx, cx.tables(), &args[1]).is_none() {
|
||||
// otherwise ignore
|
||||
Some((m, c, &args[1]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) {
|
||||
Some((m, c, &args[0]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
constant_simple(cx, cx.tables(), &args[0]).map_or_else(
|
||||
|| constant_simple(cx, cx.tables(), &args[1]).map(|c| (m, c, &args[0])),
|
||||
|c| {
|
||||
if constant_simple(cx, cx.tables(), &args[1]).is_none() {
|
||||
// otherwise ignore
|
||||
Some((m, c, &args[1]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ use rustc_ast::ast::LitKind;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty,
|
||||
TyKind, UnOp,
|
||||
self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
|
||||
StmtKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::{ExpnKind, Span};
|
||||
|
|
@ -371,8 +371,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
|||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right);
|
||||
check_to_owned(cx, right, left);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
|
|
@ -570,11 +570,30 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
matches!(&walk_ptrs_ty(cx.tables().expr_ty(expr)).kind, ty::Array(_, _))
|
||||
}
|
||||
|
||||
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
|
||||
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
|
||||
#[derive(Default)]
|
||||
struct EqImpl {
|
||||
ty_eq_other: bool,
|
||||
other_eq_ty: bool,
|
||||
}
|
||||
|
||||
impl EqImpl {
|
||||
fn is_implemented(&self) -> bool {
|
||||
self.ty_eq_other || self.other_eq_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
|
||||
cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
|
||||
ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
|
||||
other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
|
||||
})
|
||||
}
|
||||
|
||||
let (arg_ty, snip) = match expr.kind {
|
||||
ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
|
||||
if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
|
||||
(cx.tables().expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
|
||||
(cx.tables().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -582,7 +601,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
|
|||
ExprKind::Call(ref path, ref v) if v.len() == 1 => {
|
||||
if let ExprKind::Path(ref path) = path.kind {
|
||||
if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
|
||||
(cx.tables().expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
(cx.tables().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -593,28 +612,19 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
|
|||
_ => return,
|
||||
};
|
||||
|
||||
let other_ty = cx.tables().expr_ty_adjusted(other);
|
||||
let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
|
||||
Some(id) => id,
|
||||
None => return,
|
||||
};
|
||||
let other_ty = cx.tables().expr_ty(other);
|
||||
|
||||
let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| {
|
||||
implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()])
|
||||
});
|
||||
let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| {
|
||||
implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()])
|
||||
});
|
||||
let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
|
||||
let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
|
||||
let with_deref = arg_ty
|
||||
.builtin_deref(true)
|
||||
.and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
|
||||
.unwrap_or_default();
|
||||
|
||||
if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other {
|
||||
if !with_deref.is_implemented() && !without_deref.is_implemented() {
|
||||
return;
|
||||
}
|
||||
|
||||
let other_gets_derefed = match other.kind {
|
||||
ExprKind::Unary(UnOp::UnDeref, _) => true,
|
||||
_ => false,
|
||||
};
|
||||
let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::UnDeref, _));
|
||||
|
||||
let lint_span = if other_gets_derefed {
|
||||
expr.span.to(other.span)
|
||||
|
|
@ -634,18 +644,34 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
|
|||
return;
|
||||
}
|
||||
|
||||
let try_hint = if deref_arg_impl_partial_eq_other {
|
||||
// suggest deref on the left
|
||||
format!("*{}", snip)
|
||||
let expr_snip;
|
||||
let eq_impl;
|
||||
if with_deref.is_implemented() {
|
||||
expr_snip = format!("*{}", snip);
|
||||
eq_impl = with_deref;
|
||||
} else {
|
||||
// suggest dropping the to_owned on the left
|
||||
snip.to_string()
|
||||
expr_snip = snip.to_string();
|
||||
eq_impl = without_deref;
|
||||
};
|
||||
|
||||
let span;
|
||||
let hint;
|
||||
if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
|
||||
span = expr.span;
|
||||
hint = expr_snip;
|
||||
} else {
|
||||
span = expr.span.to(other.span);
|
||||
if eq_impl.ty_eq_other {
|
||||
hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
|
||||
} else {
|
||||
hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
|
||||
}
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
lint_span,
|
||||
span,
|
||||
"try",
|
||||
try_hint,
|
||||
hint,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
},
|
||||
|
|
@ -656,16 +682,10 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
|
|||
/// `unused_variables`'s idea
|
||||
/// of what it means for an expression to be "used".
|
||||
fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
match parent.kind {
|
||||
ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => {
|
||||
SpanlessEq::new(cx).eq_expr(rhs, expr)
|
||||
},
|
||||
_ => is_used(cx, parent),
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
|
||||
ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
|
||||
_ => is_used(cx, parent),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tests whether an expression is in a macro expansion (e.g., something
|
||||
|
|
@ -674,12 +694,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
|
|||
use rustc_span::hygiene::MacroKind;
|
||||
if expr.span.from_expansion() {
|
||||
let data = expr.span.ctxt().outer_expn_data();
|
||||
|
||||
if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -694,7 +709,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
|
||||
fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
|
||||
if_chain! {
|
||||
if let TyKind::Ptr(ref mut_ty) = ty.kind;
|
||||
if let ExprKind::Lit(ref lit) = e.kind;
|
||||
|
|
|
|||
|
|
@ -641,28 +641,22 @@ fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
|
|||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
|
||||
if let PatKind::Wild = pat.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
|
||||
if let Some((left_index, left_pat)) = patterns[..rest_index]
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(is_wild)
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
|
||||
}
|
||||
|
||||
if let Some((right_index, right_pat)) =
|
||||
patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
|
||||
if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
|
||||
.iter()
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -71,10 +71,11 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp
|
|||
fn is_executable(cx: &LateContext<'_>) -> bool {
|
||||
use rustc_session::config::CrateType;
|
||||
|
||||
cx.tcx.sess.crate_types().iter().any(|t: &CrateType| match t {
|
||||
CrateType::Executable => true,
|
||||
_ => false,
|
||||
})
|
||||
cx.tcx
|
||||
.sess
|
||||
.crate_types()
|
||||
.iter()
|
||||
.any(|t: &CrateType| matches!(t, CrateType::Executable))
|
||||
}
|
||||
|
||||
declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
|
||||
|
|
|
|||
|
|
@ -80,10 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
|||
// can't be implemented for unsafe new
|
||||
return;
|
||||
}
|
||||
if impl_item.generics.params.iter().any(|gen| match gen.kind {
|
||||
hir::GenericParamKind::Type { .. } => true,
|
||||
_ => false,
|
||||
}) {
|
||||
if impl_item
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
|
||||
{
|
||||
// when the result of `new()` depends on a type parameter we should not require
|
||||
// an
|
||||
// impl of `Default`
|
||||
|
|
|
|||
|
|
@ -238,10 +238,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||
|
||||
let ty = if needs_check_adjustment {
|
||||
let adjustments = cx.tables().expr_adjustments(dereferenced_expr);
|
||||
if let Some(i) = adjustments.iter().position(|adj| match adj.kind {
|
||||
Adjust::Borrow(_) | Adjust::Deref(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
if let Some(i) = adjustments
|
||||
.iter()
|
||||
.position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
|
||||
{
|
||||
if i == 0 {
|
||||
cx.tables().expr_ty(dereferenced_expr)
|
||||
} else {
|
||||
|
|
|
|||
267
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
Normal file
267
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
use crate::utils;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{match_type, paths, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
|
||||
/// idiomatically done with `Option::map_or` (if the else bit is a simple
|
||||
/// expression) or `Option::map_or_else` (if the else bit is a longer
|
||||
/// block).
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// Using the dedicated functions of the Option type is clearer and
|
||||
/// more concise than an if let expression.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// This lint uses whether the block is just an expression or if it has
|
||||
/// more statements to decide whether to use `Option::map_or` or
|
||||
/// `Option::map_or_else`. If you have a single expression which calls
|
||||
/// an expensive function, then it would be more efficient to use
|
||||
/// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
|
||||
///
|
||||
/// Also, this lint uses a deliberately conservative metric for checking
|
||||
/// if the inside of either body contains breaks or continues which will
|
||||
/// cause it to not suggest a fix if either block contains a loop with
|
||||
/// continues or breaks contained within the loop.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// # let optional: Option<u32> = Some(0);
|
||||
/// # fn do_complicated_function() -> u32 { 5 };
|
||||
/// let _ = if let Some(foo) = optional {
|
||||
/// foo
|
||||
/// } else {
|
||||
/// 5
|
||||
/// };
|
||||
/// let _ = if let Some(foo) = optional {
|
||||
/// foo
|
||||
/// } else {
|
||||
/// let y = do_complicated_function();
|
||||
/// y*y
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// should be
|
||||
///
|
||||
/// ```rust
|
||||
/// # let optional: Option<u32> = Some(0);
|
||||
/// # fn do_complicated_function() -> u32 { 5 };
|
||||
/// let _ = optional.map_or(5, |foo| foo);
|
||||
/// let _ = optional.map_or_else(||{
|
||||
/// let y = do_complicated_function();
|
||||
/// y*y
|
||||
/// }, |foo| foo);
|
||||
/// ```
|
||||
pub OPTION_IF_LET_ELSE,
|
||||
pedantic,
|
||||
"reimplementation of Option::map_or"
|
||||
}
|
||||
|
||||
declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
|
||||
|
||||
/// Returns true iff the given expression is the result of calling `Result::ok`
|
||||
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
|
||||
path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables().expr_ty(&receiver), &paths::RESULT)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct containing information about occurences of the
|
||||
/// `if let Some(..) = .. else` construct that this lint detects.
|
||||
struct OptionIfLetElseOccurence {
|
||||
option: String,
|
||||
method_sugg: String,
|
||||
some_expr: String,
|
||||
none_expr: String,
|
||||
wrap_braces: bool,
|
||||
}
|
||||
|
||||
struct ReturnBreakContinueMacroVisitor {
|
||||
seen_return_break_continue: bool,
|
||||
}
|
||||
impl ReturnBreakContinueMacroVisitor {
|
||||
fn new() -> ReturnBreakContinueMacroVisitor {
|
||||
ReturnBreakContinueMacroVisitor {
|
||||
seen_return_break_continue: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
|
||||
type Map = Map<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
if self.seen_return_break_continue {
|
||||
// No need to look farther if we've already seen one of them
|
||||
return;
|
||||
}
|
||||
match &ex.kind {
|
||||
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
|
||||
self.seen_return_break_continue = true;
|
||||
},
|
||||
// Something special could be done here to handle while or for loop
|
||||
// desugaring, as this will detect a break if there's a while loop
|
||||
// or a for loop inside the expression.
|
||||
_ => {
|
||||
if utils::in_macro(ex.span) {
|
||||
self.seen_return_break_continue = true;
|
||||
} else {
|
||||
rustc_hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||
let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
|
||||
recursive_visitor.visit_expr(expression);
|
||||
recursive_visitor.seen_return_break_continue
|
||||
}
|
||||
|
||||
/// Extracts the body of a given arm. If the arm contains only an expression,
|
||||
/// then it returns the expression. Otherwise, it returns the entire block
|
||||
fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
|
||||
if let ExprKind::Block(
|
||||
Block {
|
||||
stmts: statements,
|
||||
expr: Some(expr),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) = &arm.body.kind
|
||||
{
|
||||
if let [] = statements {
|
||||
Some(&expr)
|
||||
} else {
|
||||
Some(&arm.body)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is the else body of an if/else expression, then we need to wrap
|
||||
/// it in curcly braces. Otherwise, we don't.
|
||||
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
|
||||
if let Some(Expr {
|
||||
kind:
|
||||
ExprKind::Match(
|
||||
_,
|
||||
arms,
|
||||
MatchSource::IfDesugar {
|
||||
contains_else_clause: true,
|
||||
}
|
||||
| MatchSource::IfLetDesugar {
|
||||
contains_else_clause: true,
|
||||
},
|
||||
),
|
||||
..
|
||||
}) = parent.expr
|
||||
{
|
||||
expr.hir_id == arms[1].body.hir_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
|
||||
format!(
|
||||
"{}{}",
|
||||
Sugg::hir(cx, cond_expr, "..").maybe_par(),
|
||||
if as_mut {
|
||||
".as_mut()"
|
||||
} else if as_ref {
|
||||
".as_ref()"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// If this expression is the option if let/else construct we're detecting, then
|
||||
/// this function returns an `OptionIfLetElseOccurence` struct with details if
|
||||
/// this construct is found, or None if this construct is not found.
|
||||
fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
|
||||
if_chain! {
|
||||
if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
|
||||
if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
|
||||
if arms.len() == 2;
|
||||
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
|
||||
if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
|
||||
if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
|
||||
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
|
||||
if !contains_return_break_continue_macro(arms[0].body);
|
||||
if !contains_return_break_continue_macro(arms[1].body);
|
||||
then {
|
||||
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
|
||||
let some_body = extract_body_from_arm(&arms[0])?;
|
||||
let none_body = extract_body_from_arm(&arms[1])?;
|
||||
let method_sugg = match &none_body.kind {
|
||||
ExprKind::Block(..) => "map_or_else",
|
||||
_ => "map_or",
|
||||
};
|
||||
let capture_name = id.name.to_ident_string();
|
||||
let wrap_braces = should_wrap_in_braces(cx, expr);
|
||||
let (as_ref, as_mut) = match &cond_expr.kind {
|
||||
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
|
||||
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
|
||||
_ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
|
||||
};
|
||||
let cond_expr = match &cond_expr.kind {
|
||||
// Pointer dereferencing happens automatically, so we can omit it in the suggestion
|
||||
ExprKind::Unary(UnOp::UnDeref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
|
||||
_ => cond_expr,
|
||||
};
|
||||
Some(OptionIfLetElseOccurence {
|
||||
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
|
||||
method_sugg: method_sugg.to_string(),
|
||||
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
|
||||
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
|
||||
wrap_braces,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LateLintPass<'a> for OptionIfLetElse {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
|
||||
if let Some(detection) = detect_option_if_let_else(cx, expr) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_IF_LET_ELSE,
|
||||
expr.span,
|
||||
format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
|
||||
"try",
|
||||
format!(
|
||||
"{}{}.{}({}, {}){}",
|
||||
if detection.wrap_braces { "{ " } else { "" },
|
||||
detection.option,
|
||||
detection.method_sugg,
|
||||
detection.none_expr,
|
||||
detection.some_expr,
|
||||
if detection.wrap_braces { " }" } else { "" },
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
311
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
Normal file
311
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
use crate::utils::{last_path_segment, span_lint_and_help};
|
||||
use rustc_hir::{
|
||||
intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
|
||||
QPath, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for patterns that aren't exact representations of the types
|
||||
/// they are applied to.
|
||||
///
|
||||
/// To satisfy this lint, you will have to adjust either the expression that is matched
|
||||
/// against or the pattern itself, as well as the bindings that are introduced by the
|
||||
/// adjusted patterns. For matching you will have to either dereference the expression
|
||||
/// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
|
||||
/// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
|
||||
/// to use the inverse. You can leave them as plain bindings if you wish for the value
|
||||
/// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
|
||||
/// a reference into the matched structure.
|
||||
///
|
||||
/// If you are looking for a way to learn about ownership semantics in more detail, it
|
||||
/// is recommended to look at IDE options available to you to highlight types, lifetimes
|
||||
/// and reference semantics in your code. The available tooling would expose these things
|
||||
/// in a general way even outside of the various pattern matching mechanics. Of course
|
||||
/// this lint can still be used to highlight areas of interest and ensure a good understanding
|
||||
/// of ownership semantics.
|
||||
///
|
||||
/// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
|
||||
/// because it increases ownership hints in the code, and will guard against some changes
|
||||
/// in ownership.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// This example shows the basic adjustments necessary to satisfy the lint. Note how
|
||||
/// the matched expression is explicitly dereferenced with `*` and the `inner` variable
|
||||
/// is bound to a shared borrow via `ref inner`.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let value = &Some(Box::new(23));
|
||||
/// match value {
|
||||
/// Some(inner) => println!("{}", inner),
|
||||
/// None => println!("none"),
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// let value = &Some(Box::new(23));
|
||||
/// match *value {
|
||||
/// Some(ref inner) => println!("{}", inner),
|
||||
/// None => println!("none"),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The following example demonstrates one of the advantages of the more verbose style.
|
||||
/// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
|
||||
/// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
|
||||
/// accidentally modify the wrong part of the structure.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let mut values = vec![(2, 3), (3, 4)];
|
||||
/// for (a, b) in &mut values {
|
||||
/// *a += *b;
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// let mut values = vec![(2, 3), (3, 4)];
|
||||
/// for &mut (ref mut a, b) in &mut values {
|
||||
/// *a += b;
|
||||
/// }
|
||||
/// ```
|
||||
pub PATTERN_TYPE_MISMATCH,
|
||||
restriction,
|
||||
"type of pattern does not match the expression type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if let StmtKind::Local(ref local) = stmt.kind {
|
||||
if let Some(init) = &local.init {
|
||||
if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) {
|
||||
let pat = &local.pat;
|
||||
if in_external_macro(cx.sess(), pat.span) {
|
||||
return;
|
||||
}
|
||||
let deref_possible = match local.source {
|
||||
LocalSource::Normal => DerefPossible::Possible,
|
||||
_ => DerefPossible::Impossible,
|
||||
};
|
||||
apply_lint(cx, pat, init_ty, deref_possible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(ref expr, arms, source) = expr.kind {
|
||||
match source {
|
||||
MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
|
||||
if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) {
|
||||
'pattern_checks: for arm in arms {
|
||||
let pat = &arm.pat;
|
||||
if in_external_macro(cx.sess(), pat.span) {
|
||||
continue 'pattern_checks;
|
||||
}
|
||||
if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
|
||||
break 'pattern_checks;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
_: intravisit::FnKind<'tcx>,
|
||||
_: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
_: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) {
|
||||
for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
|
||||
apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum DerefPossible {
|
||||
Possible,
|
||||
Impossible,
|
||||
}
|
||||
|
||||
fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
|
||||
let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
|
||||
if let Some((span, mutability, level)) = maybe_mismatch {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
PATTERN_TYPE_MISMATCH,
|
||||
span,
|
||||
"type of pattern does not match the expression type",
|
||||
None,
|
||||
&format!(
|
||||
"{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
|
||||
match (deref_possible, level) {
|
||||
(DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
|
||||
_ => "",
|
||||
},
|
||||
match mutability {
|
||||
Mutability::Mut => "&mut _",
|
||||
Mutability::Not => "&_",
|
||||
},
|
||||
),
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Level {
|
||||
Top,
|
||||
Lower,
|
||||
}
|
||||
|
||||
#[allow(rustc::usage_of_ty_tykind)]
|
||||
fn find_first_mismatch<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &Pat<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
level: Level,
|
||||
) -> Option<(Span, Mutability, Level)> {
|
||||
if let PatKind::Ref(ref sub_pat, _) = pat.kind {
|
||||
if let TyKind::Ref(_, sub_ty, _) = ty.kind {
|
||||
return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
|
||||
}
|
||||
}
|
||||
|
||||
if let TyKind::Ref(_, _, mutability) = ty.kind {
|
||||
if is_non_ref_pattern(&pat.kind) {
|
||||
return Some((pat.span, mutability, level));
|
||||
}
|
||||
}
|
||||
|
||||
if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
|
||||
if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
|
||||
if let Some(variant) = get_variant(adt_def, qpath) {
|
||||
let field_defs = &variant.fields;
|
||||
return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
|
||||
if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
|
||||
if let Some(variant) = get_variant(adt_def, qpath) {
|
||||
let field_defs = &variant.fields;
|
||||
let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
|
||||
return find_first_mismatch_in_tuple(cx, pats, ty_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let PatKind::Tuple(ref pats, _) = pat.kind {
|
||||
if let TyKind::Tuple(..) = ty.kind {
|
||||
return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
|
||||
}
|
||||
}
|
||||
|
||||
if let PatKind::Or(sub_pats) = pat.kind {
|
||||
for pat in sub_pats {
|
||||
let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
|
||||
if let Some(mismatch) = maybe_mismatch {
|
||||
return Some(mismatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
|
||||
if adt_def.is_struct() {
|
||||
if let Some(variant) = adt_def.variants.iter().next() {
|
||||
return Some(variant);
|
||||
}
|
||||
}
|
||||
|
||||
if adt_def.is_enum() {
|
||||
let pat_ident = last_path_segment(qpath).ident;
|
||||
for variant in &adt_def.variants {
|
||||
if variant.ident == pat_ident {
|
||||
return Some(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn find_first_mismatch_in_tuple<'tcx, I>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pats: &[&Pat<'_>],
|
||||
ty_iter_src: I,
|
||||
) -> Option<(Span, Mutability, Level)>
|
||||
where
|
||||
I: IntoIterator<Item = Ty<'tcx>>,
|
||||
{
|
||||
let mut field_tys = ty_iter_src.into_iter();
|
||||
'fields: for pat in pats {
|
||||
let field_ty = if let Some(ty) = field_tys.next() {
|
||||
ty
|
||||
} else {
|
||||
break 'fields;
|
||||
};
|
||||
|
||||
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
|
||||
if let Some(mismatch) = maybe_mismatch {
|
||||
return Some(mismatch);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn find_first_mismatch_in_struct<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
field_pats: &[FieldPat<'_>],
|
||||
field_defs: &[FieldDef],
|
||||
substs_ref: SubstsRef<'tcx>,
|
||||
) -> Option<(Span, Mutability, Level)> {
|
||||
for field_pat in field_pats {
|
||||
'definitions: for field_def in field_defs {
|
||||
if field_pat.ident == field_def.ident {
|
||||
let field_ty = field_def.ty(cx.tcx, substs_ref);
|
||||
let pat = &field_pat.pat;
|
||||
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
|
||||
if let Some(mismatch) = maybe_mismatch {
|
||||
return Some(mismatch);
|
||||
}
|
||||
break 'definitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
|
||||
match pat_kind {
|
||||
PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
|
||||
PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -148,17 +148,11 @@ fn is_arith_expr(expr: &Expr) -> bool {
|
|||
#[must_use]
|
||||
fn is_bit_op(op: BinOpKind) -> bool {
|
||||
use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr};
|
||||
match op {
|
||||
BitXor | BitAnd | BitOr | Shl | Shr => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn is_arith_op(op: BinOpKind) -> bool {
|
||||
use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub};
|
||||
match op {
|
||||
Add | Sub | Mul | Div | Rem => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(op, Add | Sub | Mul | Div | Rem)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ declare_clippy_lint! {
|
|||
/// exclusive ranges, because they essentially add an extra branch that
|
||||
/// LLVM may fail to hoist out of the loop.
|
||||
///
|
||||
/// This will cause a warning that cannot be fixed if the consumer of the
|
||||
/// range only accepts a specific range type, instead of the generic
|
||||
/// `RangeBounds` trait
|
||||
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// for x..(y+1) { .. }
|
||||
|
|
@ -72,7 +77,10 @@ declare_clippy_lint! {
|
|||
/// **Why is this bad?** The code is more readable with an exclusive range
|
||||
/// like `x..y`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** This will cause a warning that cannot be fixed if
|
||||
/// the consumer of the range only accepts a specific range type, instead of
|
||||
/// the generic `RangeBounds` trait
|
||||
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
|
|
@ -83,7 +91,7 @@ declare_clippy_lint! {
|
|||
/// for x..y { .. }
|
||||
/// ```
|
||||
pub RANGE_MINUS_ONE,
|
||||
complexity,
|
||||
pedantic,
|
||||
"`x..=(y-1)` reads better as `x..y`"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,260 +0,0 @@
|
|||
use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_mir::const_eval::is_const_fn;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result` or
|
||||
/// `Option`
|
||||
///
|
||||
/// **Why is this bad?** It's more concise and clear to just use the proper
|
||||
/// utility function
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
/// if let Err(_) = Err::<i32, i32>(42) {}
|
||||
/// if let None = None::<()> {}
|
||||
/// if let Some(_) = Some(42) {}
|
||||
/// match Ok::<i32, i32>(42) {
|
||||
/// Ok(_) => true,
|
||||
/// Err(_) => false,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The more idiomatic use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// if Ok::<i32, i32>(42).is_ok() {}
|
||||
/// if Err::<i32, i32>(42).is_err() {}
|
||||
/// if None::<()>.is_none() {}
|
||||
/// if Some(42).is_some() {}
|
||||
/// Ok::<i32, i32>(42).is_ok();
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN_MATCHING,
|
||||
style,
|
||||
"use the proper utility function avoiding an `if let`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
|
||||
MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
|
||||
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_sugg_for_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
keyword: &'static str,
|
||||
) {
|
||||
fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
|
||||
if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
|
||||
return Some("is_ok()");
|
||||
}
|
||||
if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
|
||||
return Some("is_err()");
|
||||
}
|
||||
if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
|
||||
return Some("is_some()");
|
||||
}
|
||||
if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
|
||||
return Some("is_none()");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
let hir_id = expr.hir_id;
|
||||
let good_method = match arms[0].pat.kind {
|
||||
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_suggestion(cx, hir_id, path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
|
||||
_ => None,
|
||||
};
|
||||
let good_method = match good_method {
|
||||
Some(method) => method,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
|
||||
if method_path.ident.name == sym!(next);
|
||||
if match_trait_method(cx, op, &paths::ITERATOR);
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pat.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
// while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let expr_span = expr.span;
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// ^^^
|
||||
let op_span = op.span.source_callsite();
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(op_span.shrink_to_hi());
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||
if arms.len() == 2 {
|
||||
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
|
||||
|
||||
let hir_id = expr.hir_id;
|
||||
let found_good_method = match node_pair {
|
||||
(
|
||||
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
|
||||
PatKind::TupleStruct(ref path_right, ref patterns_right, _),
|
||||
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
|
||||
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::RESULT_OK,
|
||||
&paths::RESULT_ERR,
|
||||
"is_ok()",
|
||||
"is_err()",
|
||||
|| can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
|
||||
|| can_suggest(cx, hir_id, sym!(result_type), "is_err"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
(PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
|
||||
| (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
|
||||
if patterns.len() == 1 =>
|
||||
{
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::OPTION_SOME,
|
||||
&paths::OPTION_NONE,
|
||||
"is_some()",
|
||||
"is_none()",
|
||||
|| can_suggest(cx, hir_id, sym!(option_type), "is_some"),
|
||||
|| can_suggest(cx, hir_id, sym!(option_type), "is_none"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(good_method) = found_good_method {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
expr.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
let span = expr.span.to(op.span);
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("{}.{}", snippet(cx, op.span, "_"), good_method),
|
||||
Applicability::MaybeIncorrect, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn find_good_method_for_match<'a>(
|
||||
arms: &[Arm<'_>],
|
||||
path_left: &QPath<'_>,
|
||||
path_right: &QPath<'_>,
|
||||
expected_left: &[&str],
|
||||
expected_right: &[&str],
|
||||
should_be_left: &'a str,
|
||||
should_be_right: &'a str,
|
||||
can_suggest_left: impl Fn() -> bool,
|
||||
can_suggest_right: impl Fn() -> bool,
|
||||
) -> Option<&'a str> {
|
||||
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
|
||||
(&(*arms[0].body).kind, &(*arms[1].body).kind)
|
||||
} else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
|
||||
(&(*arms[1].body).kind, &(*arms[0].body).kind)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match body_node_pair {
|
||||
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||
(LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
|
||||
(LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
|
||||
if !in_constant(cx, hir_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
|
||||
cx.tcx
|
||||
.get_diagnostic_item(diag_item)
|
||||
.and_then(|def_id| {
|
||||
cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
|
||||
cx.tcx
|
||||
.associated_items(*imp)
|
||||
.in_definition_order()
|
||||
.find_map(|item| match item.kind {
|
||||
ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help};
|
||||
use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{LitKind, StrStyle};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{BytePos, Span};
|
||||
|
|
@ -46,66 +46,15 @@ declare_clippy_lint! {
|
|||
"trivial regular expressions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
|
||||
/// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
|
||||
/// idea anyway).
|
||||
///
|
||||
/// **Why is this bad?** Performance, at least for now. The macro version is
|
||||
/// likely to catch up long-term, but for now the dynamic version is faster.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// regex!("foo|bar")
|
||||
/// ```
|
||||
pub REGEX_MACRO,
|
||||
style,
|
||||
"use of `regex!(_)` instead of `Regex::new(_)`"
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Regex {
|
||||
spans: FxHashSet<Span>,
|
||||
last: Option<HirId>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]);
|
||||
impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Regex {
|
||||
fn check_crate(&mut self, _: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
|
||||
self.spans.clear();
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
if_chain! {
|
||||
if self.last.is_none();
|
||||
if let Some(ref expr) = block.expr;
|
||||
if match_type(cx, cx.tables().expr_ty(expr), &paths::REGEX);
|
||||
if let Some(span) = is_expn_of(expr.span, "regex");
|
||||
then {
|
||||
if !self.spans.contains(&span) {
|
||||
span_lint(
|
||||
cx,
|
||||
REGEX_MACRO,
|
||||
span,
|
||||
"`regex!(_)` found. \
|
||||
Please use `Regex::new(_)`, which is faster for now."
|
||||
);
|
||||
self.spans.insert(span);
|
||||
}
|
||||
self.last = Some(block.hir_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
if self.last.map_or(false, |id| block.hir_id == id) {
|
||||
self.last = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref fun, ref args) = expr.kind;
|
||||
|
|
@ -150,12 +99,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> {
|
|||
use regex_syntax::hir::Anchor::{EndText, StartText};
|
||||
use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
|
||||
|
||||
let is_literal = |e: &[regex_syntax::hir::Hir]| {
|
||||
e.iter().all(|e| match *e.kind() {
|
||||
Literal(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
};
|
||||
let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_)));
|
||||
|
||||
match *s.kind() {
|
||||
Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
|
||||
|
|
|
|||
82
src/tools/clippy/clippy_lints/src/repeat_once.rs
Normal file
82
src/tools/clippy/clippy_lints/src/repeat_once.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use crate::consts::{constant_context, Constant};
|
||||
use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
|
||||
/// - `.to_string()` for `str`
|
||||
/// - `.clone()` for `String`
|
||||
/// - `.to_vec()` for `slice`
|
||||
///
|
||||
/// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").repeat(1);
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").clone();
|
||||
/// }
|
||||
/// ```
|
||||
pub REPEAT_ONCE,
|
||||
complexity,
|
||||
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
|
||||
}
|
||||
|
||||
declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
|
||||
if path.ident.name == sym!(repeat);
|
||||
if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]);
|
||||
if !in_macro(args[0].span);
|
||||
then {
|
||||
let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0]));
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -259,15 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
|
|||
|
||||
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
|
||||
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
|
||||
if let Some(rpos) = fn_source.rfind("->") {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
(
|
||||
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else {
|
||||
(ty.span, Applicability::MaybeIncorrect)
|
||||
}
|
||||
fn_source
|
||||
.rfind("->")
|
||||
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
|
||||
(
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
(ty.span, Applicability::MaybeIncorrect)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -165,14 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &
|
|||
|
||||
fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
|
||||
let var_ty = cx.tables().node_type_opt(pat_id);
|
||||
if let Some(var_ty) = var_ty {
|
||||
match var_ty.kind {
|
||||
ty::Adt(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..)))
|
||||
}
|
||||
|
||||
fn check_pat<'tcx>(
|
||||
|
|
|
|||
|
|
@ -25,13 +25,7 @@ declare_clippy_lint! {
|
|||
fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match &expr.kind {
|
||||
ExprKind::Struct(..) | ExprKind::Tup(..) => true,
|
||||
ExprKind::Path(qpath) => {
|
||||
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{GenericBound, Generics, WherePredicate};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TraitBounds;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
|
||||
///
|
||||
/// **Why is this bad?** Repeating the type for every bound makes the code
|
||||
/// less readable than combining the bounds
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
|
||||
|
|
@ -29,6 +29,18 @@ declare_clippy_lint! {
|
|||
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TraitBounds {
|
||||
max_trait_bounds: u64,
|
||||
}
|
||||
|
||||
impl TraitBounds {
|
||||
#[must_use]
|
||||
pub fn new(max_trait_bounds: u64) -> Self {
|
||||
Self { max_trait_bounds }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
||||
|
|
@ -44,9 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
|||
let mut map = FxHashMap::default();
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
for bound in gen.where_clause.predicates {
|
||||
if let WherePredicate::BoundPredicate(ref p) = bound {
|
||||
if_chain! {
|
||||
if let WherePredicate::BoundPredicate(ref p) = bound;
|
||||
if p.bounds.len() as u64 <= self.max_trait_bounds;
|
||||
if !in_macro(p.span);
|
||||
let h = hash(&p.bounded_ty);
|
||||
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
|
||||
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
|
||||
|
||||
then {
|
||||
let mut hint_string = format!(
|
||||
"consider combining the bounds: `{}:",
|
||||
snippet(cx, p.bounded_ty.span, "_")
|
||||
|
|
|
|||
|
|
@ -775,11 +775,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg {
|
|||
.iter()
|
||||
.filter(|arg| {
|
||||
if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) {
|
||||
if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
!matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -899,17 +895,11 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn is_unit(ty: Ty<'_>) -> bool {
|
||||
match ty.kind {
|
||||
ty::Tuple(slice) if slice.is_empty() => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(ty.kind, ty::Tuple(slice) if slice.is_empty())
|
||||
}
|
||||
|
||||
fn is_unit_literal(expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Tup(ref slice) if slice.is_empty() => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -1154,10 +1144,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
|
|||
}
|
||||
|
||||
fn is_isize_or_usize(typ: Ty<'_>) -> bool {
|
||||
match typ.kind {
|
||||
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
|
||||
}
|
||||
|
||||
fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
|
||||
|
|
@ -1205,16 +1192,19 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast
|
|||
// has parens on the outside, they are no longer needed.
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let opt = snippet_opt(cx, op.span);
|
||||
let sugg = if let Some(ref snip) = opt {
|
||||
if should_strip_parens(op, snip) {
|
||||
&snip[1..snip.len() - 1]
|
||||
} else {
|
||||
snip.as_str()
|
||||
}
|
||||
} else {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
".."
|
||||
};
|
||||
let sugg = opt.as_ref().map_or_else(
|
||||
|| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
".."
|
||||
},
|
||||
|snip| {
|
||||
if should_strip_parens(op, snip) {
|
||||
&snip[1..snip.len() - 1]
|
||||
} else {
|
||||
snip.as_str()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
@ -1734,10 +1724,10 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
|
|||
|
||||
TyKind::TraitObject(ref param_bounds, _) => {
|
||||
let has_lifetime_parameters = param_bounds.iter().any(|bound| {
|
||||
bound.bound_generic_params.iter().any(|gen| match gen.kind {
|
||||
GenericParamKind::Lifetime { .. } => true,
|
||||
_ => false,
|
||||
})
|
||||
bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
|
||||
});
|
||||
if has_lifetime_parameters {
|
||||
// complex trait bounds like A<'a, 'b>
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COM
|
|||
impl LateLintPass<'_> for UnnamedAddress {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
fn is_comparison(binop: BinOpKind) -> bool {
|
||||
match binop {
|
||||
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(
|
||||
binop,
|
||||
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt
|
||||
)
|
||||
}
|
||||
|
||||
fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
|
|
@ -72,11 +72,7 @@ impl LateLintPass<'_> for UnnamedAddress {
|
|||
}
|
||||
|
||||
fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(cx.tables().expr_ty(expr).kind, ty::FnDef(..))
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
|
|
|
|||
|
|
@ -5,24 +5,23 @@ use if_chain::if_chain;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Detects when people use `Vec::sort_by` and pass in a function
|
||||
/// Detects uses of `Vec::sort_by` passing in a closure
|
||||
/// which compares the two arguments, either directly or indirectly.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
|
||||
/// possible) than to use `Vec::sort_by` and and a more complicated
|
||||
/// possible) than to use `Vec::sort_by` and a more complicated
|
||||
/// closure.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't
|
||||
/// imported by a use statement in the current frame, then a `use`
|
||||
/// statement that imports it will need to be added (which this lint
|
||||
/// can't do).
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
|
||||
/// imported by a use statement, then it will need to be added manually.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
|
|
@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
|||
};
|
||||
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
|
||||
let unstable = name == "sort_unstable_by";
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
then {
|
||||
Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
|
||||
}
|
||||
else {
|
||||
Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
vec_name,
|
||||
unstable,
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse
|
||||
}))
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
|
||||
} else {
|
||||
if !key_returns_borrow(cx, left_expr) {
|
||||
return Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
vec_name,
|
||||
unstable,
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(def_id) = utils::fn_def_id(cx, expr) {
|
||||
let output = cx.tcx.fn_sig(def_id).output();
|
||||
let ty = output.skip_binder();
|
||||
return matches!(ty.kind, ty::Ref(..))
|
||||
|| ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for UnnecessarySortBy {
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ impl EarlyLintPass for UnnestedOrPatterns {
|
|||
}
|
||||
|
||||
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if !cx.sess.opts.unstable_features.is_nightly_build() {
|
||||
// User cannot do `#![feature(or_patterns)]`, so bail.
|
||||
if !cx.sess.features_untracked().or_patterns {
|
||||
// Do not suggest nesting the patterns if the feature `or_patterns` is not enabled.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -400,8 +400,8 @@ fn extend_with_matching(
|
|||
|
||||
/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
|
||||
fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
|
||||
ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
|
||||
&& ps1.len() == ps2.len()
|
||||
ps1.len() == ps2.len()
|
||||
&& ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
|
||||
&& over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
|
||||
&& over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,14 +167,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind;
|
||||
then {
|
||||
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
|
||||
let should_check = if let Some(ref params) = *parameters {
|
||||
!params.parenthesized && !params.args.iter().any(|arg| match arg {
|
||||
GenericArg::Lifetime(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
true
|
||||
};
|
||||
let should_check = parameters.as_ref().map_or(
|
||||
true,
|
||||
|params| !params.parenthesized
|
||||
&&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
);
|
||||
|
||||
if should_check {
|
||||
let visitor = &mut UseSelfVisitor {
|
||||
|
|
|
|||
|
|
@ -387,10 +387,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
|
|||
}
|
||||
|
||||
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
|
||||
match (l, r) {
|
||||
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)))
|
||||
}
|
||||
|
||||
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
|
||||
|
|
|
|||
|
|
@ -65,42 +65,45 @@ pub fn get_attr<'a>(
|
|||
};
|
||||
let attr_segments = &attr.path.segments;
|
||||
if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
|
||||
if let Some(deprecation_status) =
|
||||
BUILTIN_ATTRIBUTES
|
||||
.iter()
|
||||
.find_map(|(builtin_name, deprecation_status)| {
|
||||
if *builtin_name == attr_segments[1].ident.to_string() {
|
||||
Some(deprecation_status)
|
||||
} else {
|
||||
None
|
||||
BUILTIN_ATTRIBUTES
|
||||
.iter()
|
||||
.find_map(|(builtin_name, deprecation_status)| {
|
||||
if *builtin_name == attr_segments[1].ident.to_string() {
|
||||
Some(deprecation_status)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map_or_else(
|
||||
|| {
|
||||
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
|
||||
false
|
||||
},
|
||||
|deprecation_status| {
|
||||
let mut diag =
|
||||
sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
|
||||
match *deprecation_status {
|
||||
DeprecationStatus::Deprecated => {
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::Replaced(new_name) => {
|
||||
diag.span_suggestion(
|
||||
attr_segments[1].ident.span,
|
||||
"consider using",
|
||||
new_name.to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::None => {
|
||||
diag.cancel();
|
||||
attr_segments[1].ident.to_string() == name
|
||||
},
|
||||
}
|
||||
})
|
||||
{
|
||||
let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
|
||||
match *deprecation_status {
|
||||
DeprecationStatus::Deprecated => {
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::Replaced(new_name) => {
|
||||
diag.span_suggestion(
|
||||
attr_segments[1].ident.span,
|
||||
"consider using",
|
||||
new_name.to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::None => {
|
||||
diag.cancel();
|
||||
attr_segments[1].ident.to_string() == name
|
||||
},
|
||||
}
|
||||
} else {
|
||||
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
|
||||
false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ define_Conf! {
|
|||
(array_size_threshold, "array_size_threshold": u64, 512_000),
|
||||
/// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
|
||||
(vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
|
||||
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
|
||||
(max_trait_bounds, "max_trait_bounds": u64, 3),
|
||||
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
|
||||
(max_struct_bools, "max_struct_bools": u64, 3),
|
||||
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
|
||||
|
|
|
|||
|
|
@ -703,6 +703,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
}
|
||||
for segment in path.segments {
|
||||
segment.ident.name.hash(&mut self.s);
|
||||
self.hash_generic_args(segment.generic_args().args);
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
|
|
@ -711,13 +712,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
},
|
||||
},
|
||||
TyKind::OpaqueDef(_, arg_list) => {
|
||||
for arg in *arg_list {
|
||||
match arg {
|
||||
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
|
||||
GenericArg::Type(ref ty) => self.hash_ty(&ty),
|
||||
GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
|
||||
}
|
||||
}
|
||||
self.hash_generic_args(arg_list);
|
||||
},
|
||||
TyKind::TraitObject(_, lifetime) => {
|
||||
self.hash_lifetime(lifetime);
|
||||
|
|
@ -735,4 +730,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
|
||||
self.maybe_typeck_tables = old_maybe_typeck_tables;
|
||||
}
|
||||
|
||||
fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
|
||||
for arg in arg_list {
|
||||
match arg {
|
||||
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
|
||||
GenericArg::Type(ref ty) => self.hash_ty(&ty),
|
||||
GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,11 +102,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
|
|||
#[must_use]
|
||||
pub fn in_macro(span: Span) -> bool {
|
||||
if span.from_expansion() {
|
||||
if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
!matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -127,10 +123,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
|
|||
|
||||
/// Checks if given pattern is a wildcard (`_`)
|
||||
pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Wild => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(pat.kind, PatKind::Wild)
|
||||
}
|
||||
|
||||
/// Checks if type is struct, enum or union type with the given def path.
|
||||
|
|
@ -153,11 +146,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
|
|||
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
|
||||
let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
let trt_id = cx.tcx.trait_of_item(def_id);
|
||||
if let Some(trt_id) = trt_id {
|
||||
match_def_path(cx, trt_id, path)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
|
||||
}
|
||||
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
|
|
@ -600,21 +589,15 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
|
|||
/// // ^^^^^^^^^^
|
||||
/// ```
|
||||
pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
|
||||
if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
|
||||
span.with_lo(first_char_pos)
|
||||
} else {
|
||||
span
|
||||
}
|
||||
first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
|
||||
}
|
||||
|
||||
fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
|
||||
let line_span = line_span(cx, span);
|
||||
if let Some(snip) = snippet_opt(cx, line_span) {
|
||||
snippet_opt(cx, line_span).and_then(|snip| {
|
||||
snip.find(|c: char| !c.is_whitespace())
|
||||
.map(|pos| line_span.lo() + BytePos::from_usize(pos))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the indentation of the line of a span
|
||||
|
|
@ -626,11 +609,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
|
|||
/// // ^^ -- will return 4
|
||||
/// ```
|
||||
pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
|
||||
if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
|
||||
snip.find(|c: char| !c.is_whitespace())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
|
||||
}
|
||||
|
||||
/// Extends the span to the beginning of the spans line, incl. whitespaces.
|
||||
|
|
@ -738,25 +717,21 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
|||
let enclosing_node = map
|
||||
.get_enclosing_scope(hir_id)
|
||||
.and_then(|enclosing_id| map.find(enclosing_id));
|
||||
if let Some(node) = enclosing_node {
|
||||
match node {
|
||||
Node::Block(block) => Some(block),
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Fn(_, _, eid),
|
||||
..
|
||||
})
|
||||
| Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(_, eid),
|
||||
..
|
||||
}) => match cx.tcx.hir().body(eid).value.kind {
|
||||
ExprKind::Block(ref block, _) => Some(block),
|
||||
_ => None,
|
||||
},
|
||||
enclosing_node.and_then(|node| match node {
|
||||
Node::Block(block) => Some(block),
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Fn(_, _, eid),
|
||||
..
|
||||
})
|
||||
| Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(_, eid),
|
||||
..
|
||||
}) => match cx.tcx.hir().body(eid).value.kind {
|
||||
ExprKind::Block(ref block, _) => Some(block),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the base type for HIR references and pointers.
|
||||
|
|
@ -1328,11 +1303,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(did) = did {
|
||||
must_use_attr(&cx.tcx.get_attrs(did)).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
|
||||
}
|
||||
|
||||
pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
|
||||
|
|
@ -1385,6 +1356,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns the `DefId` of the callee if the given expression is a function or method call.
|
||||
pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(qpath),
|
||||
..
|
||||
},
|
||||
..,
|
||||
) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
|
||||
lints.iter().any(|lint| {
|
||||
matches!(
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl<'a> NumericLiteral<'a> {
|
|||
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
|
||||
if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
|
||||
let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
|
||||
let float = if let LitKind::Float(..) = lit_kind { true } else { false };
|
||||
let float = matches!(lit_kind, LitKind::Float(..));
|
||||
Some(NumericLiteral::new(unsuffixed, suffix, float))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -200,12 +200,10 @@ impl<'a> NumericLiteral<'a> {
|
|||
|
||||
fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
|
||||
debug_assert!(lit_kind.is_numeric());
|
||||
if let Some(suffix_length) = lit_suffix_length(lit_kind) {
|
||||
lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
|
||||
let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
|
||||
(unsuffixed, Some(suffix))
|
||||
} else {
|
||||
(src, None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
|
|||
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
|
||||
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
|
||||
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
|
||||
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
|
||||
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
|
||||
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
|
||||
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
||||
|
|
|
|||
|
|
@ -325,22 +325,22 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
|
|||
/// parenthesis will always be added for a mix of these.
|
||||
pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
|
||||
/// Returns `true` if the operator is a shift operator `<<` or `>>`.
|
||||
fn is_shift(op: &AssocOp) -> bool {
|
||||
matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
|
||||
fn is_shift(op: AssocOp) -> bool {
|
||||
matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
|
||||
}
|
||||
|
||||
/// Returns `true` if the operator is a arithmetic operator
|
||||
/// (i.e., `+`, `-`, `*`, `/`, `%`).
|
||||
fn is_arith(op: &AssocOp) -> bool {
|
||||
fn is_arith(op: AssocOp) -> bool {
|
||||
matches!(
|
||||
*op,
|
||||
op,
|
||||
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the operator `op` needs parenthesis with the operator
|
||||
/// `other` in the direction `dir`.
|
||||
fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
|
||||
fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
|
||||
other.precedence() < op.precedence()
|
||||
|| (other.precedence() == op.precedence()
|
||||
&& ((op != other && associativity(op) != dir)
|
||||
|
|
@ -349,14 +349,14 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
|
|||
|| is_shift(other) && is_arith(op)
|
||||
}
|
||||
|
||||
let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
|
||||
needs_paren(&op, lop, Associativity::Left)
|
||||
let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
|
||||
needs_paren(op, lop, Associativity::Left)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
|
||||
needs_paren(&op, rop, Associativity::Right)
|
||||
let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
|
||||
needs_paren(op, rop, Associativity::Right)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
@ -424,13 +424,13 @@ enum Associativity {
|
|||
/// they are considered
|
||||
/// associative.
|
||||
#[must_use]
|
||||
fn associativity(op: &AssocOp) -> Associativity {
|
||||
fn associativity(op: AssocOp) -> Associativity {
|
||||
use rustc_ast::util::parser::AssocOp::{
|
||||
Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
|
||||
GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
|
||||
};
|
||||
|
||||
match *op {
|
||||
match op {
|
||||
Assign | AssignOp(_) => Associativity::Right,
|
||||
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
|
||||
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
|
||||
|
|
@ -492,20 +492,20 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
|
|||
/// before it on its line.
|
||||
fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
let lo = cx.sess().source_map().lookup_char_pos(span.lo());
|
||||
if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
|
||||
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
|
||||
// We can mix char and byte positions here because we only consider `[ \t]`.
|
||||
if lo.col == CharPos(pos) {
|
||||
Some(line[..pos].into())
|
||||
lo.file
|
||||
.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
|
||||
.and_then(|line| {
|
||||
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
|
||||
// We can mix char and byte positions here because we only consider `[ \t]`.
|
||||
if lo.col == CharPos(pos) {
|
||||
Some(line[..pos].into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Convenience extension trait for `DiagnosticBuilder`.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue