From 99e6a28804eac57faa37134d61a2bb17069996a2 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Thu, 30 May 2024 18:32:46 -0400 Subject: [PATCH 001/806] Add f16/f128 handling in a couple places --- compiler/rustc_codegen_llvm/src/abi.rs | 2 ++ compiler/rustc_target/src/abi/call/mod.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a6a3f0f964611..2e52d3f426a83 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -121,8 +121,10 @@ impl LlvmType for Reg { match self.kind { RegKind::Integer => cx.type_ix(self.size.bits()), RegKind::Float => match self.size.bits() { + 16 => cx.type_f16(), 32 => cx.type_f32(), 64 => cx.type_f64(), + 128 => cx.type_f128(), _ => bug!("unsupported float: {:?}", self), }, RegKind::Vector => cx.type_vector(cx.type_i8(), self.size.bytes()), diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index fc79c9232d1bd..f83d0492004a2 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -236,8 +236,10 @@ impl Reg { _ => panic!("unsupported integer: {self:?}"), }, RegKind::Float => match self.size.bits() { + 16 => dl.f16_align.abi, 32 => dl.f32_align.abi, 64 => dl.f64_align.abi, + 128 => dl.f128_align.abi, _ => panic!("unsupported float: {self:?}"), }, RegKind::Vector => dl.vector_align(self.size).abi, From 24ce341185d929e0c3f14cce73ce7aac9ede0bf8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 2 Jun 2024 16:43:29 +0200 Subject: [PATCH 002/806] from_ref, from_mut: clarify domain of quantification --- library/core/src/ptr/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8e3447d0b1b24..f1a1b1a414935 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -777,8 +777,8 @@ where /// Convert a reference to a raw pointer. /// -/// This is equivalent to `r as *const T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will +/// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -791,8 +791,8 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// -/// This is equivalent to `r as *mut T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will +/// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] From 64e7337be596cb12aa8c7f593a1fe5d17ab089ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 4 Jun 2024 07:37:24 +0200 Subject: [PATCH 003/806] more explicitly state the basic rules of working with the obtained raw pointers --- library/core/src/ptr/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f1a1b1a414935..c6af6617bab79 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -779,6 +779,15 @@ where /// /// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will /// never silently change type or mutability, in particular if the code is refactored. +/// +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up pointing to garbage. +/// +/// The caller must also ensure that the memory the pointer (non-transitively) points to is never +/// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If +/// you need to mutate the pointee, use [`from_mut`]`. Specifically, to turn a mutable reference `m: +/// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be +/// used for mutation. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -791,6 +800,9 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up pointing to garbage. +/// /// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will /// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] From 05b7f282e8a883005237833f3bbc972072111e74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 6 Jun 2024 08:04:34 +0200 Subject: [PATCH 004/806] less garbage, more examples --- library/alloc/src/vec/mod.rs | 4 +- library/core/src/ptr/mod.rs | 80 ++++++++++++++++++++++++++++++++--- library/core/src/slice/mod.rs | 4 +- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index aa9b632cbed9e..7ecc8855dbc68 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1276,7 +1276,7 @@ impl Vec { /// valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// @@ -1336,7 +1336,7 @@ impl Vec { /// raw pointer valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index c6af6617bab79..0a20b3c04c8ea 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -777,17 +777,51 @@ where /// Convert a reference to a raw pointer. /// -/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will -/// never silently change type or mutability, in particular if the code is refactored. +/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T` (except for the caveat noted below), +/// but is a bit safer since it will never silently change type or mutability, in particular if the +/// code is refactored. /// /// The caller must ensure that the pointee outlives the pointer this function returns, or else it -/// will end up pointing to garbage. +/// will end up dangling. /// /// The caller must also ensure that the memory the pointer (non-transitively) points to is never /// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If /// you need to mutate the pointee, use [`from_mut`]`. Specifically, to turn a mutable reference `m: /// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be /// used for mutation. +/// +/// ## Interaction with lifetime extension +/// +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &foo() as *const T; +/// unsafe { p.read() }; +/// ``` +/// Naively replacing the cast with `from_ref` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_ref(&foo()); +/// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let x = foo(); +/// let p = ptr::from_ref(&x); +/// unsafe { p.read() }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -800,11 +834,45 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// +/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T` (except for the caveat noted +/// below), but is a bit safer since it will never silently change type or mutability, in particular +/// if the code is refactored. +/// /// The caller must ensure that the pointee outlives the pointer this function returns, or else it -/// will end up pointing to garbage. +/// will end up dangling. +/// +/// ## Interaction with lifetime extension /// -/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will -/// never silently change type or mutability, in particular if the code is refactored. +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &mut foo() as *mut T; +/// unsafe { p.write(T::default()) }; +/// ``` +/// Naively replacing the cast with `from_mut` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_mut(&mut foo()); +/// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let mut x = foo(); +/// let p = ptr::from_mut(&mut x); +/// unsafe { p.write(T::default()) }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 503107c74803b..8ada77c34b353 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -731,7 +731,7 @@ impl [T] { /// Returns a raw pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// The caller must also ensure that the memory the pointer (non-transitively) points to /// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer @@ -766,7 +766,7 @@ impl [T] { /// Returns an unsafe mutable pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// Modifying the container referenced by this slice may cause its buffer /// to be reallocated, which would also make any pointers to it invalid. From 46b84956f8391b90a4c140a85270571954309bbf Mon Sep 17 00:00:00 2001 From: ilikdoge Date: Sun, 23 Jun 2024 13:43:46 -0700 Subject: [PATCH 005/806] Implement `unsigned_signed_diff` --- library/core/src/num/uint_macros.rs | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1491c27372bfb..81062f9161349 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -726,6 +726,67 @@ macro_rules! uint_impl { } } + #[doc = concat!( + "Checked integer subtraction. Computes `self - rhs` and checks if the result fits into an [`", + stringify!($SignedT), "`], returning `None` if overflow occurred." + )] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(unsigned_signed_diff)] + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_signed_diff(2), Some(8));")] + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_signed_diff(10), Some(-8));")] + #[doc = concat!( + "assert_eq!(", + stringify!($SelfT), + "::MAX.checked_signed_diff(", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + "), None);" + )] + #[doc = concat!( + "assert_eq!((", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + ").checked_signed_diff(", + stringify!($SelfT), + "::MAX), Some(", + stringify!($SignedT), + "::MIN));" + )] + #[doc = concat!( + "assert_eq!((", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + " + 1).checked_signed_diff(0), None);" + )] + #[doc = concat!( + "assert_eq!(", + stringify!($SelfT), + "::MAX.checked_signed_diff(", + stringify!($SelfT), + "::MAX), Some(0));" + )] + /// ``` + #[unstable(feature = "unsigned_signed_diff", issue = "126041")] + #[inline] + pub const fn checked_signed_diff(self, rhs: Self) -> Option<$SignedT> { + let res = self.wrapping_sub(rhs) as $SignedT; + let overflow = (self >= rhs) == (res < 0); + + if !overflow { + Some(res) + } else { + None + } + } + /// Checked integer multiplication. Computes `self * rhs`, returning /// `None` if overflow occurred. /// From 414ebeaeea13d37bcd59473d11660169286b7cd5 Mon Sep 17 00:00:00 2001 From: Stephen Skeirik Date: Mon, 17 Jun 2024 17:01:38 -0400 Subject: [PATCH 006/806] add serde derive Serialize to stable_mir --- Cargo.lock | 1 + compiler/stable_mir/Cargo.toml | 1 + compiler/stable_mir/src/abi.rs | 35 ++--- compiler/stable_mir/src/crate_def.rs | 3 +- compiler/stable_mir/src/lib.rs | 10 +- compiler/stable_mir/src/mir/alloc.rs | 5 +- compiler/stable_mir/src/mir/body.rs | 81 +++++------ compiler/stable_mir/src/mir/mono.rs | 10 +- compiler/stable_mir/src/target.rs | 7 +- compiler/stable_mir/src/ty.rs | 153 ++++++++++++--------- tests/ui-fulldeps/stable-mir/smir_serde.rs | 75 ++++++++++ 11 files changed, 242 insertions(+), 139 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/smir_serde.rs diff --git a/Cargo.lock b/Cargo.lock index 241a37588b409..24caf5cd3e67f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5254,6 +5254,7 @@ name = "stable_mir" version = "0.1.0-preview" dependencies = [ "scoped-tls", + "serde", ] [[package]] diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml index 4ed6115273653..2edb3f140d7ac 100644 --- a/compiler/stable_mir/Cargo.toml +++ b/compiler/stable_mir/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] scoped-tls = "1.0" +serde = { version = "1.0.125", features = [ "derive" ] } diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs index e1c14fe0b380b..c003ec1fbc920 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/stable_mir/src/abi.rs @@ -5,12 +5,13 @@ use crate::target::{MachineInfo, MachineSize as Size}; use crate::ty::{Align, IndexedVal, Ty, VariantIdx}; use crate::Error; use crate::Opaque; +use serde::Serialize; use std::fmt::{self, Debug}; use std::num::NonZero; use std::ops::RangeInclusive; /// A function ABI definition. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct FnAbi { /// The types of each argument. pub args: Vec, @@ -31,7 +32,7 @@ pub struct FnAbi { } /// Information about the ABI of a function's argument, or return value. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct ArgAbi { pub ty: Ty, pub layout: Layout, @@ -39,7 +40,7 @@ pub struct ArgAbi { } /// How a function argument should be passed in to the target function. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum PassMode { /// Ignore the argument. /// @@ -60,14 +61,14 @@ pub enum PassMode { } /// The layout of a type, alongside the type itself. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct TyAndLayout { pub ty: Ty, pub layout: Layout, } /// The layout of a type in memory. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct LayoutShape { /// The fields location withing the layout pub fields: FieldsShape, @@ -108,7 +109,7 @@ impl LayoutShape { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct Layout(usize); impl Layout { @@ -127,7 +128,7 @@ impl IndexedVal for Layout { } /// Describes how the fields of a type are shaped in memory. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum FieldsShape { /// Scalar primitives and `!`, which never have fields. Primitive, @@ -177,7 +178,7 @@ impl FieldsShape { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum VariantsShape { /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { index: VariantIdx }, @@ -196,7 +197,7 @@ pub enum VariantsShape { }, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum TagEncoding { /// The tag directly stores the discriminant, but possibly with a smaller layout /// (so converting the tag to the discriminant can require sign extension). @@ -221,7 +222,7 @@ pub enum TagEncoding { /// Describes how values of the type are passed by target ABIs, /// in terms of categories of C types there are ABI rules for. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum ValueAbi { Uninhabited, Scalar(Scalar), @@ -250,7 +251,7 @@ impl ValueAbi { } /// Information about one scalar component of a Rust type. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] pub enum Scalar { Initialized { /// The primitive type used to represent this value. @@ -280,7 +281,7 @@ impl Scalar { } /// Fundamental unit of memory access and layout. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] pub enum Primitive { /// The `bool` is the signedness of the `Integer` type. /// @@ -310,7 +311,7 @@ impl Primitive { } /// Enum representing the existing integer lengths. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] pub enum IntegerLength { I8, I16, @@ -320,7 +321,7 @@ pub enum IntegerLength { } /// Enum representing the existing float lengths. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] pub enum FloatLength { F16, F32, @@ -354,7 +355,7 @@ impl FloatLength { /// An identifier that specifies the address space that some operation /// should operate on. Special address spaces have an effect on code generation, /// depending on the target and the address spaces it implements. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct AddressSpace(pub u32); impl AddressSpace { @@ -369,7 +370,7 @@ impl AddressSpace { /// sequence: /// /// 254 (-2), 255 (-1), 0, 1, 2 -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] pub struct WrappingRange { pub start: u128, pub end: u128, @@ -420,7 +421,7 @@ impl Debug for WrappingRange { } /// General language calling conventions. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum CallConvention { C, Rust, diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 67752a5e629f3..a7cebc4c132ba 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -3,9 +3,10 @@ use crate::ty::{GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; +use serde::Serialize; /// A unique identification number for each item accessible for the current compilation unit. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] pub struct DefId(pub(crate) usize); /// A trait for retrieving information about a particular definition. diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 8385856ae532d..fe745326d819a 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -27,6 +27,7 @@ pub use crate::error::*; use crate::mir::Body; use crate::mir::Mutability; use crate::ty::{ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; +use serde::Serialize; pub mod abi; #[macro_use] @@ -74,7 +75,7 @@ pub type TraitDecls = Vec; pub type ImplTraitDecls = Vec; /// Holds information about a crate. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] pub struct Crate { pub id: CrateNum, pub name: Symbol, @@ -98,7 +99,7 @@ impl Crate { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] pub enum ItemKind { Fn, Static, @@ -106,7 +107,7 @@ pub enum ItemKind { Ctor(CtorKind), } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] pub enum CtorKind { Const, Fn, @@ -116,6 +117,7 @@ pub type Filename = String; crate_def_with_ty! { /// Holds information about an item in a crate. + #[derive(Serialize)] pub CrateItem; } @@ -188,7 +190,7 @@ pub fn all_trait_impls() -> ImplTraitDecls { } /// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub struct Opaque(String); impl std::fmt::Display for Opaque { diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index ef1568151f2e6..9e0cac67f0aa3 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -4,11 +4,12 @@ use crate::mir::mono::{Instance, StaticDef}; use crate::target::{Endian, MachineInfo}; use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; use crate::{with, Error}; +use serde::Serialize; use std::io::Read; /// An allocation in the SMIR global memory can be either a function pointer, /// a static, or a "real" allocation with some data in it. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub enum GlobalAlloc { /// The alloc ID is used as a function pointer. Function(Instance), @@ -41,7 +42,7 @@ impl GlobalAlloc { } /// A unique identification number for each provenance -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] pub struct AllocId(usize); impl IndexedVal for AllocId { diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index e0f9e7ae67a0f..f7457ecd38f4c 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -5,10 +5,11 @@ use crate::ty::{ TyConst, TyKind, VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; +use serde::Serialize; use std::io; /// The SMIR representation of a single function. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct Body { pub blocks: Vec, @@ -104,20 +105,20 @@ impl Body { type LocalDecls = Vec; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct LocalDecl { pub ty: Ty, pub span: Span, pub mutability: Mutability, } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] pub struct BasicBlock { pub statements: Vec, pub terminator: Terminator, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Terminator { pub kind: TerminatorKind, pub span: Span, @@ -131,7 +132,7 @@ impl Terminator { pub type Successors = Vec; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum TerminatorKind { Goto { target: BasicBlockIdx, @@ -221,7 +222,7 @@ impl TerminatorKind { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct InlineAsmOperand { pub in_value: Option, pub out_place: Option, @@ -230,7 +231,7 @@ pub struct InlineAsmOperand { pub raw_rpr: String, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum UnwindAction { Continue, Unreachable, @@ -238,7 +239,7 @@ pub enum UnwindAction { Cleanup(BasicBlockIdx), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum AssertMessage { BoundsCheck { len: Operand, index: Operand }, Overflow(BinOp, Operand, Operand), @@ -307,7 +308,7 @@ impl AssertMessage { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum BinOp { Add, AddUnchecked, @@ -342,7 +343,7 @@ impl BinOp { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum UnOp { Not, Neg, @@ -357,20 +358,20 @@ impl UnOp { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum CoroutineKind { Desugared(CoroutineDesugaring, CoroutineSource), Coroutine(Movability), } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum CoroutineSource { Block, Closure, Fn, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum CoroutineDesugaring { Async, @@ -386,7 +387,7 @@ pub(crate) type LocalDefId = Opaque; pub(crate) type Coverage = Opaque; /// The FakeReadCause describes the type of pattern why a FakeRead statement exists. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum FakeReadCause { ForMatchGuard, ForMatchedPlace(LocalDefId), @@ -396,7 +397,7 @@ pub enum FakeReadCause { } /// Describes what kind of retag is to be performed -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub enum RetagKind { FnEntry, TwoPhase, @@ -404,7 +405,7 @@ pub enum RetagKind { Default, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub enum Variance { Covariant, Invariant, @@ -412,26 +413,26 @@ pub enum Variance { Bivariant, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct CopyNonOverlapping { pub src: Operand, pub dst: Operand, pub count: Operand, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Statement { pub kind: StatementKind, pub span: Span, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum StatementKind { Assign(Place, Rvalue), FakeRead(FakeReadCause, Place), @@ -448,7 +449,7 @@ pub enum StatementKind { Nop, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum Rvalue { /// Creates a pointer with the indicated mutability to the place. /// @@ -622,7 +623,7 @@ impl Rvalue { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum AggregateKind { Array(Ty), Tuple, @@ -633,14 +634,14 @@ pub enum AggregateKind { RawPtr(Ty, Mutability), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum Operand { Copy(Place), Move(Place), Constant(ConstOperand), } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Serialize)] pub struct Place { pub local: Local, /// projection out of a place (access a field, deref a pointer, etc) @@ -653,7 +654,7 @@ impl From for Place { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ConstOperand { pub span: Span, pub user_ty: Option, @@ -661,7 +662,7 @@ pub struct ConstOperand { } /// Debug information pertaining to a user variable. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct VarDebugInfo { /// The variable name. pub name: Symbol, @@ -703,19 +704,19 @@ impl VarDebugInfo { pub type SourceScope = u32; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct SourceInfo { pub span: Span, pub scope: SourceScope, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct VarDebugInfoFragment { pub ty: Ty, pub projection: Vec, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum VarDebugInfoContents { Place(Place), Const(ConstOperand), @@ -726,7 +727,7 @@ pub enum VarDebugInfoContents { // ProjectionElem) and user-provided type annotations (for which the projection elements // are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use // ProjectionElem for Places. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ProjectionElem { /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. Deref, @@ -800,7 +801,7 @@ pub enum ProjectionElem { Subtype(Ty), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, @@ -830,7 +831,7 @@ pub type FieldIdx = usize; type UserTypeAnnotationIndex = usize; /// The possible branch sites of a [TerminatorKind::SwitchInt]. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct SwitchTargets { /// The conditional branches where the first element represents the value that guards this /// branch, and the second element is the branch target. @@ -867,7 +868,7 @@ impl SwitchTargets { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, @@ -894,14 +895,14 @@ impl BorrowKind { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum MutBorrowKind { Default, TwoPhaseBorrow, ClosureCapture, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum FakeBorrowKind { /// A shared (deep) borrow. Data must be immutable and is aliasable. Deep, @@ -912,19 +913,19 @@ pub enum FakeBorrowKind { Shallow, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum Mutability { Not, Mut, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum Safety { Safe, Unsafe, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum PointerCoercion { /// Go from a fn-item type to a fn-pointer type. ReifyFnPointer, @@ -951,7 +952,7 @@ pub enum PointerCoercion { Unsize, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum CastKind { // FIXME(smir-rename): rename this to PointerExposeProvenance PointerExposeAddress, @@ -967,7 +968,7 @@ pub enum CastKind { Transmute, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum NullOp { /// Returns the size of a value of that type. SizeOf, diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 572f1499c5a5c..c23293388f933 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -3,17 +3,18 @@ use crate::crate_def::CrateDef; use crate::mir::Body; use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque, Symbol}; +use serde::Serialize; use std::fmt::{Debug, Formatter}; use std::io; -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum MonoItem { Fn(Instance), Static(StaticDef), GlobalAsm(Opaque), } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)] pub struct Instance { /// The type of instance. pub kind: InstanceKind, @@ -22,7 +23,7 @@ pub struct Instance { pub def: InstanceDef, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum InstanceKind { /// A user defined item. Item, @@ -240,7 +241,7 @@ impl From for CrateItem { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] pub struct InstanceDef(usize); impl CrateDef for InstanceDef { @@ -251,6 +252,7 @@ impl CrateDef for InstanceDef { crate_def! { /// Holds information about a static variable definition. + #[derive(Serialize)] pub StaticDef; } diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs index e00a418c54039..9fb5e046abc3c 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/stable_mir/src/target.rs @@ -1,9 +1,10 @@ //! Provide information about the machine that this is being compiled into. use crate::compiler_interface::with; +use serde::Serialize; /// The properties of the target machine being compiled into. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Serialize)] pub struct MachineInfo { pub endian: Endian, pub pointer_width: MachineSize, @@ -23,14 +24,14 @@ impl MachineInfo { } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Serialize)] pub enum Endian { Little, Big, } /// Represent the size of a component. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] pub struct MachineSize { num_bits: usize, } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 8c120a96e75b1..9c8921da52ff9 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -8,10 +8,11 @@ use crate::mir::alloc::{read_target_int, read_target_uint, AllocId}; use crate::mir::mono::StaticDef; use crate::target::MachineInfo; use crate::{Filename, Opaque}; +use serde::Serialize; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] pub struct Ty(usize); impl Debug for Ty { @@ -100,13 +101,13 @@ impl Ty { } /// Represents a pattern in the type system -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum Pattern { Range { start: Option, end: Option, include_end: bool }, } /// Represents a constant in the type system -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct TyConst { pub(crate) kind: TyConstKind, pub id: TyConstId, @@ -133,7 +134,7 @@ impl TyConst { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum TyConstKind { Param(ParamConst), Bound(DebruijnIndex, BoundVar), @@ -144,11 +145,11 @@ pub enum TyConstKind { ZSTValue(Ty), } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub struct TyConstId(usize); /// Represents a constant in MIR -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct MirConst { /// The constant kind. pub(crate) kind: ConstantKind, @@ -205,17 +206,17 @@ impl MirConst { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub struct MirConstId(usize); type Ident = Opaque; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Region { pub kind: RegionKind, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum RegionKind { ReEarlyParam(EarlyParamRegion), ReBound(DebruijnIndex, BoundRegion), @@ -226,7 +227,7 @@ pub enum RegionKind { pub(crate) type DebruijnIndex = u32; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct EarlyParamRegion { pub index: u32, pub name: Symbol, @@ -234,7 +235,7 @@ pub struct EarlyParamRegion { pub(crate) type BoundVar = u32; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct BoundRegion { pub var: BoundVar, pub kind: BoundRegionKind, @@ -242,13 +243,13 @@ pub struct BoundRegion { pub(crate) type UniverseIndex = u32; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Placeholder { pub universe: UniverseIndex, pub bound: T, } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize)] pub struct Span(usize); impl Debug for Span { @@ -272,7 +273,7 @@ impl Span { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Serialize)] /// Information you get from `Span` in a struct form. /// Line and col start from 1. pub struct LineInfo { @@ -282,7 +283,7 @@ pub struct LineInfo { pub end_col: usize, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum TyKind { RigidTy(RigidTy), Alias(AliasKind, AliasTy), @@ -521,7 +522,7 @@ pub struct TypeAndMut { pub mutability: Mutability, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum RigidTy { Bool, Char, @@ -560,7 +561,7 @@ impl From for TyKind { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum IntTy { Isize, I8, @@ -583,7 +584,7 @@ impl IntTy { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum UintTy { Usize, U8, @@ -606,19 +607,20 @@ impl UintTy { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum FloatTy { F32, F64, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum Movability { Static, Movable, } crate_def! { + #[derive(Serialize)] pub ForeignModuleDef; } @@ -641,6 +643,7 @@ impl ForeignModule { crate_def_with_ty! { /// Hold information about a ForeignItem in a crate. + #[derive(Serialize)] pub ForeignDef; } @@ -650,7 +653,7 @@ impl ForeignDef { } } -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] pub enum ForeignItemKind { Fn(FnDef), Static(StaticDef), @@ -659,6 +662,7 @@ pub enum ForeignItemKind { crate_def_with_ty! { /// Hold information about a function definition in a crate. + #[derive(Serialize)] pub FnDef; } @@ -692,6 +696,7 @@ impl FnDef { } crate_def_with_ty! { + #[derive(Serialize)] pub IntrinsicDef; } @@ -716,26 +721,31 @@ impl From for FnDef { } crate_def! { + #[derive(Serialize)] pub ClosureDef; } crate_def! { + #[derive(Serialize)] pub CoroutineDef; } crate_def! { + #[derive(Serialize)] pub ParamDef; } crate_def! { + #[derive(Serialize)] pub BrNamedDef; } -crate_def_with_ty! { +crate_def! { + #[derive(Serialize)] pub AdtDef; } -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] pub enum AdtKind { Enum, Union, @@ -789,7 +799,7 @@ impl AdtDef { } /// Definition of a variant, which can be either a struct / union field or an enum variant. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] pub struct VariantDef { /// The variant index. /// @@ -818,7 +828,7 @@ impl VariantDef { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct FieldDef { /// The field definition. /// @@ -869,11 +879,13 @@ impl AdtKind { } crate_def! { + #[derive(Serialize)] pub AliasDef; } crate_def! { /// A trait's definition. + #[derive(Serialize)] pub TraitDef; } @@ -884,15 +896,18 @@ impl TraitDef { } crate_def! { + #[derive(Serialize)] pub GenericDef; } crate_def_with_ty! { + #[derive(Serialize)] pub ConstDef; } crate_def! { /// A trait impl definition. + #[derive(Serialize)] pub ImplDef; } @@ -904,15 +919,17 @@ impl ImplDef { } crate_def! { + #[derive(Serialize)] pub RegionDef; } crate_def! { + #[derive(Serialize)] pub CoroutineWitnessDef; } /// A list of generic arguments. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct GenericArgs(pub Vec); impl std::ops::Index for GenericArgs { @@ -931,7 +948,7 @@ impl std::ops::Index for GenericArgs { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum GenericArgKind { Lifetime(Region), Type(Ty), @@ -968,13 +985,13 @@ impl GenericArgKind { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum TermKind { Type(Ty), Const(TyConst), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum AliasKind { Projection, Inherent, @@ -982,13 +999,13 @@ pub enum AliasKind { Weak, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct AliasTy { pub def_id: AliasDef, pub args: GenericArgs, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct AliasTerm { pub def_id: AliasDef, pub args: GenericArgs, @@ -1006,7 +1023,7 @@ impl PolyFnSig { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct FnSig { pub inputs_and_output: Vec, pub c_variadic: bool, @@ -1024,7 +1041,7 @@ impl FnSig { } } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] pub enum Abi { Rust, C { unwind: bool }, @@ -1054,7 +1071,7 @@ pub enum Abi { } /// A binder represents a possibly generic type and its bound vars. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Binder { pub value: T, pub bound_vars: Vec, @@ -1094,38 +1111,38 @@ impl Binder { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct EarlyBinder { pub value: T, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum BoundVariableKind { Ty(BoundTyKind), Region(BoundRegionKind), Const, } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] pub enum BoundTyKind { Anon, Param(ParamDef, String), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum BoundRegionKind { BrAnon, BrNamed(BrNamedDef, String), BrEnv, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum DynKind { Dyn, DynStar, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ExistentialPredicate { Trait(ExistentialTraitRef), Projection(ExistentialProjection), @@ -1135,7 +1152,7 @@ pub enum ExistentialPredicate { /// An existential reference to a trait where `Self` is not included. /// /// The `generic_args` will include any other known argument. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ExistentialTraitRef { pub def_id: TraitDef, pub generic_args: GenericArgs, @@ -1153,20 +1170,20 @@ impl ExistentialTraitRef { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ExistentialProjection { pub def_id: TraitDef, pub generic_args: GenericArgs, pub term: TermKind, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ParamTy { pub index: u32, pub name: String, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct BoundTy { pub var: usize, pub kind: BoundTyKind, @@ -1177,7 +1194,7 @@ pub type Bytes = Vec>; /// Size in bytes. pub type Size = usize; -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] pub struct Prov(pub AllocId); pub type Align = u64; @@ -1185,14 +1202,14 @@ pub type Promoted = u32; pub type InitMaskMaterialized = Vec; /// Stores the provenance information of pointers stored in memory. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub struct ProvenanceMap { /// Provenance in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. pub ptrs: Vec<(Size, Prov)>, } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub struct Allocation { pub bytes: Bytes, pub provenance: ProvenanceMap, @@ -1268,7 +1285,7 @@ impl Allocation { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ConstantKind { Ty(TyConst), Allocated(Allocation), @@ -1279,27 +1296,27 @@ pub enum ConstantKind { ZeroSized, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ParamConst { pub index: u32, pub name: String, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct UnevaluatedConst { pub def: ConstDef, pub args: GenericArgs, pub promoted: Option, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum TraitSpecializationKind { None, Marker, AlwaysApplicable, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct TraitDecl { pub def_id: TraitDef, pub safety: Safety, @@ -1332,7 +1349,7 @@ impl TraitDecl { pub type ImplTrait = EarlyBinder; /// A complete reference to a trait, i.e., one where `Self` is known. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct TraitRef { pub def_id: TraitDef, /// The generic arguments for this definition. @@ -1366,7 +1383,7 @@ impl TraitRef { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Generics { pub parent: Option, pub parent_count: usize, @@ -1377,14 +1394,14 @@ pub struct Generics { pub host_effect_index: Option, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum GenericParamDefKind { Lifetime, Type { has_default: bool, synthetic: bool }, Const { has_default: bool }, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct GenericParamDef { pub name: super::Symbol, pub def_id: GenericDef, @@ -1398,7 +1415,7 @@ pub struct GenericPredicates { pub predicates: Vec<(PredicateKind, Span)>, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum PredicateKind { Clause(ClauseKind), ObjectSafe(TraitDef), @@ -1409,7 +1426,7 @@ pub enum PredicateKind { AliasRelate(TermKind, TermKind, AliasRelationDirection), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ClauseKind { Trait(TraitPredicate), RegionOutlives(RegionOutlivesPredicate), @@ -1420,57 +1437,57 @@ pub enum ClauseKind { ConstEvaluatable(TyConst), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ClosureKind { Fn, FnMut, FnOnce, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct SubtypePredicate { pub a: Ty, pub b: Ty, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct CoercePredicate { pub a: Ty, pub b: Ty, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum AliasRelationDirection { Equate, Subtype, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct TraitPredicate { pub trait_ref: TraitRef, pub polarity: PredicatePolarity, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct OutlivesPredicate(pub A, pub B); pub type RegionOutlivesPredicate = OutlivesPredicate; pub type TypeOutlivesPredicate = OutlivesPredicate; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ProjectionPredicate { pub projection_term: AliasTerm, pub term: TermKind, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ImplPolarity { Positive, Negative, Reservation, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum PredicatePolarity { Positive, Negative, @@ -1513,7 +1530,7 @@ index_impl!(Span); /// `a` is in the variant with the `VariantIdx` of `0`, /// `c` is in the variant with the `VariantIdx` of `1`, and /// `g` is in the variant with the `VariantIdx` of `0`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] pub struct VariantIdx(usize); index_impl!(VariantIdx); diff --git a/tests/ui-fulldeps/stable-mir/smir_serde.rs b/tests/ui-fulldeps/stable-mir/smir_serde.rs new file mode 100644 index 0000000000000..957d840f7a241 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/smir_serde.rs @@ -0,0 +1,75 @@ +//@ run-pass +//! Test that users are able to use serialize stable MIR constructs. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +//@ edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_middle; +extern crate stable_mir; +extern crate serde; +extern crate serde_json; + +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::mir::Body; +use std::io::{Write, BufWriter}; +use std::ops::ControlFlow; +use serde_json::to_string; + + +const CRATE_NAME: &str = "input"; + +fn serialize_to_json(_tcx: TyCtxt<'_>) -> ControlFlow<()> { + let path = "output.json"; + let mut writer = BufWriter::new(std::fs::File::create(path) + .expect("Failed to create path")); + let local_crate = stable_mir::local_crate(); + let items: Vec = stable_mir::all_local_items() + .iter() + .map(|item| { item.body() }) + .collect(); + let crate_data = ( local_crate.name, items ); + writer.write_all(to_string(&crate_data) + .expect("serde_json failed") + .as_bytes()).expect("JSON serialization failed"); + ControlFlow::Continue(()) +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "internal_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run_with_tcx!(args, serialize_to_json).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + pub fn main() {{ + }} + "# + )?; + Ok(()) +} From 8449d10a29b09c0852660ee168a77083d8179cf2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 27 Jun 2024 14:11:54 +0800 Subject: [PATCH 007/806] Extend rules of dead code analysis for impls for adts to impls for types refer to adts --- compiler/rustc_passes/src/dead.rs | 32 ++++++++----- .../dead-code/unused-impl-for-non-adts.rs | 45 +++++++++++++++++++ .../dead-code/unused-impl-for-non-adts.stderr | 20 +++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.rs create mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bbd586386dd27..0a3b6ae06df3a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -54,7 +54,24 @@ impl Publicness { } } -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { +fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { + match ty.kind { + TyKind::Path(hir::QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + { + Some((local_def_id, def_kind)) + } else { + None + } + } + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), + _ => None, + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness @@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { /// for enum and union, just check they are public, /// and doesn't solve types like &T for now, just skip them fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - { + if let Some((def_id, def_kind)) = adt_of(ty) { return match def_kind { DefKind::Enum | DefKind::Union => { let ty_is_public = tcx.visibility(def_id).is_public(); @@ -584,10 +598,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir().item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() + if let Some((local_def_id, def_kind)) = + adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id @@ -931,7 +943,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs new file mode 100644 index 0000000000000..46065dcee81e0 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs @@ -0,0 +1,45 @@ +#![deny(dead_code)] + +struct Foo; //~ ERROR struct `Foo` is never constructed + +trait Trait { //~ ERROR trait `Trait` is never used + fn foo(&self) {} +} + +impl Trait for Foo {} + +impl Trait for [Foo] {} +impl Trait for [Foo; N] {} + +impl Trait for *const Foo {} +impl Trait for *mut Foo {} + +impl Trait for &Foo {} +impl Trait for &&Foo {} +impl Trait for &mut Foo {} + +impl Trait for [&Foo] {} +impl Trait for &[Foo] {} +impl Trait for &*const Foo {} + +pub trait Trait2 { + fn foo(&self) {} +} + +impl Trait2 for Foo {} + +impl Trait2 for [Foo] {} +impl Trait2 for [Foo; N] {} + +impl Trait2 for *const Foo {} +impl Trait2 for *mut Foo {} + +impl Trait2 for &Foo {} +impl Trait2 for &&Foo {} +impl Trait2 for &mut Foo {} + +impl Trait2 for [&Foo] {} +impl Trait2 for &[Foo] {} +impl Trait2 for &*const Foo {} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr new file mode 100644 index 0000000000000..e61fc403e810d --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr @@ -0,0 +1,20 @@ +error: struct `Foo` is never constructed + --> $DIR/unused-impl-for-non-adts.rs:3:8 + | +LL | struct Foo; + | ^^^ + | +note: the lint level is defined here + --> $DIR/unused-impl-for-non-adts.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: trait `Trait` is never used + --> $DIR/unused-impl-for-non-adts.rs:5:7 + | +LL | trait Trait { + | ^^^^^ + +error: aborting due to 2 previous errors + From 1f41fbe0ef6685a97d9d04056c2de3020aa81775 Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 1 Jul 2024 20:39:48 +0100 Subject: [PATCH 008/806] introduce tests --- tests/ui/dropck/const_drop_is_valid.rs | 11 + tests/ui/dropck/const_drop_is_valid.stderr | 45 ++++ .../constrained_by_assoc_type_equality.rs | 12 + .../constrained_by_assoc_type_equality.stderr | 15 ++ .../unconstrained_const_param_on_drop.rs | 8 + .../unconstrained_const_param_on_drop.stderr | 219 ++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 tests/ui/dropck/const_drop_is_valid.rs create mode 100644 tests/ui/dropck/const_drop_is_valid.stderr create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality.rs create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality.stderr create mode 100644 tests/ui/dropck/unconstrained_const_param_on_drop.rs create mode 100644 tests/ui/dropck/unconstrained_const_param_on_drop.stderr diff --git a/tests/ui/dropck/const_drop_is_valid.rs b/tests/ui/dropck/const_drop_is_valid.rs new file mode 100644 index 0000000000000..0441b6ed067a4 --- /dev/null +++ b/tests/ui/dropck/const_drop_is_valid.rs @@ -0,0 +1,11 @@ +#![feature(effects)] +//~^ WARN: the feature `effects` is incomplete + +struct A(); + +impl const Drop for A {} +//~^ ERROR: const trait impls are experimental +//~| const `impl` for trait `Drop` which is not marked with `#[const_trait]` +//~| not all trait items implemented, missing: `drop` + +fn main() {} diff --git a/tests/ui/dropck/const_drop_is_valid.stderr b/tests/ui/dropck/const_drop_is_valid.stderr new file mode 100644 index 0000000000000..f15b7ba946dbf --- /dev/null +++ b/tests/ui/dropck/const_drop_is_valid.stderr @@ -0,0 +1,45 @@ +error[E0658]: const trait impls are experimental + --> $DIR/const_drop_is_valid.rs:6:6 + | +LL | impl const Drop for A {} + | ^^^^^ + | + = note: see issue #67792 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/const_drop_is_valid.rs:1:12 + | +LL | #![feature(effects)] + | ^^^^^^^ + | + = note: see issue #102090 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: using `#![feature(effects)]` without enabling next trait solver globally + | + = note: the next trait solver must be enabled globally for the effects feature to work correctly + = help: use `-Znext-solver` to enable + +error: const `impl` for trait `Drop` which is not marked with `#[const_trait]` + --> $DIR/const_drop_is_valid.rs:6:12 + | +LL | impl const Drop for A {} + | ^^^^ + | + = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` + = note: adding a non-const method body in the future would be a breaking change + +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/const_drop_is_valid.rs:6:1 + | +LL | impl const Drop for A {} + | ^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0658. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.rs b/tests/ui/dropck/constrained_by_assoc_type_equality.rs new file mode 100644 index 0000000000000..521806cc1d7ac --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.rs @@ -0,0 +1,12 @@ +struct Foo(T); + +trait Trait { + type Assoc; +} + +impl, U: ?Sized> Drop for Foo { + //~^ ERROR: `Drop` impl requires `::Assoc == U` + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr b/tests/ui/dropck/constrained_by_assoc_type_equality.stderr new file mode 100644 index 0000000000000..29f4881c3fbef --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.stderr @@ -0,0 +1,15 @@ +error[E0367]: `Drop` impl requires `::Assoc == U` but the struct it is implemented for does not + --> $DIR/constrained_by_assoc_type_equality.rs:7:15 + | +LL | impl, U: ?Sized> Drop for Foo { + | ^^^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/constrained_by_assoc_type_equality.rs:1:1 + | +LL | struct Foo(T); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.rs b/tests/ui/dropck/unconstrained_const_param_on_drop.rs new file mode 100644 index 0000000000000..369ec7280bd36 --- /dev/null +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.rs @@ -0,0 +1,8 @@ +//@ known-bug: unknown +//@ failure-status: 101 + +struct Foo {} + +impl Drop for Foo {} + +fn main() {} diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr new file mode 100644 index 0000000000000..b48ae8cc8af21 --- /dev/null +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr @@ -0,0 +1,219 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/ty/sty.rs:360:36: +called `Option::unwrap()` on a `None` value +stack backtrace: + 0: begin_panic_handler + at ./library/std/src/panicking.rs:661:5 + 1: panic_fmt + at ./library/core/src/panicking.rs:74:14 + 2: panic + at ./library/core/src/panicking.rs:148:5 + 3: core::option::unwrap_failed + at ./library/core/src/option.rs:2013:5 + 4: unwrap + at ./library/core/src/option.rs:963:21 + 5: find_ty_from_env + at ./compiler/rustc_middle/src/ty/sty.rs:360:36 + 6: process_obligation + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:472:29 + 7: process_obligations + at ./compiler/rustc_data_structures/src/obligation_forest/mod.rs:462:23 + 8: select + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:107:13 + 9: select_where_possible + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:160:9 + 10: select_all_or_error, rustc_trait_selection::traits::FulfillmentError> + at ./compiler/rustc_infer/src/traits/engine.rs:82:22 + 11: select_all_or_error + at ./compiler/rustc_trait_selection/src/traits/engine.rs:189:9 + 12: ensure_drop_predicates_are_implied_by_item_defn + at ./compiler/rustc_hir_analysis/src/check/dropck.rs:154:18 + 13: check_drop_impl + at ./compiler/rustc_hir_analysis/src/check/dropck.rs:60:13 + 14: call core::result::Result<(), rustc_span::ErrorGuaranteed>, (rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)> + at ./library/core/src/ops/function.rs:79:5 + 15: {closure#0} core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/util.rs:360:16 + 16: for_each_relevant_impl core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./compiler/rustc_middle/src/ty/trait_def.rs:166:21 + 17: calculate_dtor core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/util.rs:359:9 + 18: rustc_hir_analysis::check::adt_destructor + at ./compiler/rustc_hir_analysis/src/check/mod.rs:125:5 + 19: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:285:13 + [... omitted 22 frames ...] + 20: query_get_at>> + at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 + 21: adt_destructor + at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 + 22: adt_destructor + at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 + 23: destructor + at ./compiler/rustc_middle/src/ty/adt.rs:610:13 + 24: check_struct + at ./compiler/rustc_hir_analysis/src/check/check.rs:71:5 + 25: check_item_type + at ./compiler/rustc_hir_analysis/src/check/check.rs:730:13 + 26: check_item + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:335:5 + 27: check_well_formed + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:192:39 + 28: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 29: query_ensure_error_guaranteed>, ()> + at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 + 30: check_well_formed + at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 + 31: {closure#1} + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1995:47 + 32: {closure#0} + at ./compiler/rustc_middle/src/hir/mod.rs:89:57 + 33: {closure#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:203:50 + 34: call_once, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}>> + at ./library/core/src/panic/unwind_safe.rs:272:9 + 35: do_call>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/panicking.rs:553:40 + 36: try, core::panic::unwind_safe::AssertUnwindSafe>>> + at ./library/std/src/panicking.rs:517:19 + 37: catch_unwind>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/panic.rs:350:14 + 38: run, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#2}::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 + 39: {closure#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:46 + 40: {closure#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/core/src/iter/adapters/filter_map.rs:39:28 + 41: fold, core::iter::adapters::filter_map::filter_map_fold::{closure_env#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./library/core/src/slice/iter/macros.rs:232:27 + 42: fold, core::slice::iter::Iter, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/core/src/iter/adapters/filter_map.rs:148:9 + 43: {closure#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:73 + 44: parallel_guard, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 + 45: try_par_for_each_in<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:199:9 + 46: par_items + at ./compiler/rustc_middle/src/hir/mod.rs:75:9 + 47: check_mod_type_wf + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1994:19 + 48: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 49: query_ensure_error_guaranteed>, ()> + at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 + 50: check_mod_type_wf + at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 + 51: {closure#0} + at ./compiler/rustc_hir_analysis/src/lib.rs:162:21 + 52: {closure#0} + at ./compiler/rustc_middle/src/hir/map/mod.rs:463:13 + 53: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:182:34 + 54: call_once<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./library/core/src/panic/unwind_safe.rs:272:9 + 55: do_call>>, ()> + at ./library/std/src/panicking.rs:553:40 + 56: try<(), core::panic::unwind_safe::AssertUnwindSafe>>> + at ./library/std/src/panicking.rs:517:19 + 57: catch_unwind>>, ()> + at ./library/std/src/panic.rs:350:14 + 58: run<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#1}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 + 59: {closure#1}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:186:21 + 60: for_each>> + at ./library/core/src/slice/iter/macros.rs:254:21 + 61: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:185:17 + 62: parallel_guard<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 + 63: par_for_each_in<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:178:9 + 64: par_for_each_module + at ./compiler/rustc_middle/src/hir/map/mod.rs:462:9 + 65: {closure#0} + at ./compiler/rustc_hir_analysis/src/lib.rs:161:9 + 66: run<(), rustc_hir_analysis::check_crate::{closure_env#0}> + at ./compiler/rustc_data_structures/src/profiling.rs:754:9 + 67: time<(), rustc_hir_analysis::check_crate::{closure_env#0}> + at ./compiler/rustc_session/src/utils.rs:16:9 + 68: check_crate + at ./compiler/rustc_hir_analysis/src/lib.rs:160:5 + 69: run_required_analyses + at ./compiler/rustc_interface/src/passes.rs:784:5 + 70: analysis + at ./compiler/rustc_interface/src/passes.rs:823:5 + 71: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 72: query_get_at>> + at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 + 73: analysis + at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 + 74: analysis + at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 + 75: {closure#5} + at ./compiler/rustc_driver_impl/src/lib.rs:445:52 + 76: {closure#1}> + at ./compiler/rustc_middle/src/ty/context.rs:1296:37 + 77: {closure#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/context/tls.rs:82:9 + 78: try_with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/thread/local.rs:283:12 + 79: with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/thread/local.rs:260:9 + 80: enter_context>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/context/tls.rs:79:9 + 81: enter> + at ./compiler/rustc_middle/src/ty/context.rs:1296:9 + 82: {closure#1} + at ./compiler/rustc_driver_impl/src/lib.rs:445:13 + 83: enter, rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/queries.rs:202:19 + 84: {closure#0} + at ./compiler/rustc_driver_impl/src/lib.rs:389:22 + 85: {closure#1}, rustc_driver_impl::run_compiler::{closure_env#0}> + at ./compiler/rustc_interface/src/interface.rs:502:27 + 86: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:154:13 + 87: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:106:21 + 88: set, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at /home/boxy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/scoped-tls-1.0.1/src/lib.rs:137:9 + 89: create_session_globals_then, rustc_interface::util::run_in_thread_with_globals::{closure#0}::{closure#0}::{closure_env#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./compiler/rustc_span/src/lib.rs:134:5 + 90: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:105:17 +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + +error: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc 1.81.0-dev running on x86_64-unknown-linux-gnu + +note: compiler flags: -Z threads=1 -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ignore-directory-in-diagnostics-source-blocks=/home/boxy/.cargo -Z ignore-directory-in-diagnostics-source-blocks=/media/Nyoomies/Repos/rust/vendor -C codegen-units=1 -Z ui-testing -Z deduplicate-diagnostics=no -Z write-long-types-to-disk=no -C strip=debuginfo -C prefer-dynamic -C rpath -C debuginfo=0 + +query stack during panic: +#0 [adt_destructor] computing `Drop` impl for `Foo` +#1 [check_well_formed] checking that `Foo` is well-formed +#2 [check_mod_type_wf] checking that types are well-formed in top-level module +#3 [analysis] running analysis passes on this crate +end of query stack +error[E0207]: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained_const_param_on_drop.rs:6:6 + | +LL | impl Drop for Foo {} + | ^^^^^^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. From 6a0000827671ab12837a5a3a692d5dd9c31def69 Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 1 Jul 2024 21:43:00 +0100 Subject: [PATCH 009/806] Rewrite dropck --- .../rustc_hir_analysis/src/check/dropck.rs | 34 ++- .../constrained_by_assoc_type_equality.rs | 3 +- ...ined_by_assoc_type_equality_and_self_ty.rs | 12 + ...by_assoc_type_equality_and_self_ty.stderr} | 10 +- .../dropck/reject-specialized-drops-8142.rs | 202 ++++++++++------ .../reject-specialized-drops-8142.stderr | 133 +++++------ .../ui/dropck/transitive-outlives.bad.stderr | 9 +- tests/ui/dropck/transitive-outlives.rs | 2 +- .../unconstrained_const_param_on_drop.rs | 5 +- .../unconstrained_const_param_on_drop.stderr | 224 ++---------------- 10 files changed, 266 insertions(+), 368 deletions(-) create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs rename tests/ui/dropck/{constrained_by_assoc_type_equality.stderr => constrained_by_assoc_type_equality_and_self_ty.stderr} (53%) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 1937144802554..06ec01484a412 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -6,10 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::util::CheckRegions; -use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{GenericArgsRef, Ty}; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -115,8 +115,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( Err(err.emit()) } -/// Confirms that every predicate imposed by dtor_predicates is -/// implied by assuming the predicates attached to self_type_did. +/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be +/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are +/// implied by the ADT being well formed. fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_def_id: LocalDefId, @@ -126,6 +127,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let impl_span = tcx.def_span(drop_impl_def_id.to_def_id()); + // Take the param-env of the adt and instantiate the args that show up in // the implementation's self type. This gives us the assumptions that the // self ty of the implementation is allowed to know just from it being a @@ -135,14 +138,27 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // We don't need to normalize this param-env or anything, since we're only // instantiating it with free params, so no additional param-env normalization // can occur on top of what has been done in the param_env query itself. - let param_env = + // + // Note: Ideally instead of instantiating the `ParamEnv` with the arguments from the impl ty we + // could instead use identity args for the adt. Unfortunately this would cause any errors to + // reference the params from the ADT instead of from the impl which is bad UX. To resolve + // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. + let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); + let adt_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args); - for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) { + let fresh_impl_args = infcx.fresh_args_for_item(impl_span, drop_impl_def_id.to_def_id()); + let fresh_adt_ty = + tcx.impl_trait_ref(drop_impl_def_id).unwrap().instantiate(tcx, fresh_impl_args).self_ty(); + + ocx.eq(&ObligationCause::dummy_with_span(impl_span), adt_env, fresh_adt_ty, impl_adt_ty) + .unwrap(); + + for (clause, span) in tcx.predicates_of(drop_impl_def_id).instantiate(tcx, fresh_impl_args) { let normalize_cause = traits::ObligationCause::misc(span, adt_def_id); - let pred = ocx.normalize(&normalize_cause, param_env, pred); + let pred = ocx.normalize(&normalize_cause, adt_env, clause); let cause = traits::ObligationCause::new(span, adt_def_id, ObligationCauseCode::DropImpl); - ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred)); + ocx.register_obligation(traits::Obligation::new(tcx, cause, adt_env, pred)); } // All of the custom error reporting logic is to preserve parity with the old @@ -176,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( return Err(guar.unwrap()); } - let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env)); + let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(adt_env)); if !errors.is_empty() { let mut guar = None; for error in errors { diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.rs b/tests/ui/dropck/constrained_by_assoc_type_equality.rs index 521806cc1d7ac..4101fe83c3bea 100644 --- a/tests/ui/dropck/constrained_by_assoc_type_equality.rs +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.rs @@ -1,3 +1,5 @@ +//@ check-pass + struct Foo(T); trait Trait { @@ -5,7 +7,6 @@ trait Trait { } impl, U: ?Sized> Drop for Foo { - //~^ ERROR: `Drop` impl requires `::Assoc == U` fn drop(&mut self) {} } diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs new file mode 100644 index 0000000000000..3a85b86cb1fdd --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs @@ -0,0 +1,12 @@ +trait Trait { + type Assoc; +} + +struct Foo(T, U); + +impl, U: ?Sized> Drop for Foo { + //~^ ERROR: `Drop` impl requires `::Assoc == U` + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr similarity index 53% rename from tests/ui/dropck/constrained_by_assoc_type_equality.stderr rename to tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr index 29f4881c3fbef..dab8c55d0e7f3 100644 --- a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr +++ b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr @@ -1,14 +1,14 @@ error[E0367]: `Drop` impl requires `::Assoc == U` but the struct it is implemented for does not - --> $DIR/constrained_by_assoc_type_equality.rs:7:15 + --> $DIR/constrained_by_assoc_type_equality_and_self_ty.rs:7:15 | -LL | impl, U: ?Sized> Drop for Foo { +LL | impl, U: ?Sized> Drop for Foo { | ^^^^^^^^^ | note: the implementor must specify the same requirement - --> $DIR/constrained_by_assoc_type_equality.rs:1:1 + --> $DIR/constrained_by_assoc_type_equality_and_self_ty.rs:5:1 | -LL | struct Foo(T); - | ^^^^^^^^^^^^^^^^^^^^ +LL | struct Foo(T, U); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/dropck/reject-specialized-drops-8142.rs b/tests/ui/dropck/reject-specialized-drops-8142.rs index 7a3bbe7cb0932..1b73fe9a06550 100644 --- a/tests/ui/dropck/reject-specialized-drops-8142.rs +++ b/tests/ui/dropck/reject-specialized-drops-8142.rs @@ -1,75 +1,145 @@ // Issue 8142: Test that Drop impls cannot be specialized beyond the // predicates attached to the type definition itself. -trait Bound { fn foo(&self) { } } -struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } -struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } -struct M<'m> { x: &'m i8 } -struct N<'n> { x: &'n i8 } -struct O { x: *const To } -struct P { x: *const Tp } -struct Q { x: *const Tq } -struct R { x: *const Tr } -struct S { x: *const Ts } -struct T<'t,Ts:'t> { x: &'t Ts } +trait Bound { + fn foo(&self) {} +} +struct K<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} +struct L<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} +struct M<'m> { + x: &'m i8, +} +struct N<'n> { + x: &'n i8, +} +struct O { + x: *const To, +} +struct P { + x: *const Tp, +} +struct Q { + x: *const Tq, +} +struct R { + x: *const Tr, +} +struct S { + x: *const Ts, +} +struct T<'t, Ts: 't> { + x: &'t Ts, +} struct U; -struct V { x: *const Tva, y: *const Tvb } -struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } +struct V { + x: *const Tva, + y: *const Tvb, +} +struct W<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} struct X; struct Y; -enum Enum { Variant(T) } +enum Enum { + Variant(T), +} struct TupleStruct(T); -union Union { f: T } +union Union { + f: T, +} -impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT +impl<'al, 'adds_bnd: 'al> Drop for K<'al, 'adds_bnd> { //~^ ERROR `Drop` impl requires `'adds_bnd: 'al` - fn drop(&mut self) { } } - -impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT - //~^ ERROR `Drop` impl requires `'adds_bnd: 'al` - fn drop(&mut self) { } } - -impl<'ml> Drop for M<'ml> { fn drop(&mut self) { } } // ACCEPT - -impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for O { fn drop(&mut self) { } } // ACCEPT - -impl Drop for P { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Q { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl<'rbnd,AddsRBnd:'rbnd> Drop for R { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsRBnd: 'rbnd` - -impl Drop for S { fn drop(&mut self) { } } // ACCEPT - -impl<'t,Bt:'t> Drop for T<'t,Bt> { fn drop(&mut self) { } } // ACCEPT - -impl Drop for U { fn drop(&mut self) { } } // ACCEPT - -impl Drop for V { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for X<3> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Y { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Enum { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl Drop for TupleStruct { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl Drop for Union { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -pub fn main() { } + fn drop(&mut self) {} +} + +impl<'al, 'adds_bnd> Drop for L<'al, 'adds_bnd> +//~^ ERROR `Drop` impl requires `'adds_bnd: 'al` +where + 'adds_bnd: 'al, +{ + fn drop(&mut self) {} +} + +impl<'ml> Drop for M<'ml> { + fn drop(&mut self) {} +} + +impl Drop for N<'static> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for O { + fn drop(&mut self) {} +} + +impl Drop for P { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Q { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl<'rbnd, AddsRBnd: 'rbnd> Drop for R { + fn drop(&mut self) {} +} + +impl Drop for S { + fn drop(&mut self) {} +} + +impl<'t, Bt: 't> Drop for T<'t, Bt> { + fn drop(&mut self) {} +} + +impl Drop for U { + fn drop(&mut self) {} +} + +impl Drop for V { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl<'lw> Drop for W<'lw, 'lw> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for X<3> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Y { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Enum { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl Drop for TupleStruct { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl Drop for Union { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +pub fn main() {} diff --git a/tests/ui/dropck/reject-specialized-drops-8142.stderr b/tests/ui/dropck/reject-specialized-drops-8142.stderr index cb48221c67a82..9c8b6d544639a 100644 --- a/tests/ui/dropck/reject-specialized-drops-8142.stderr +++ b/tests/ui/dropck/reject-specialized-drops-8142.stderr @@ -1,166 +1,157 @@ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:24:20 + --> $DIR/reject-specialized-drops-8142.rs:58:1 | -LL | impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT - | ^^^ +LL | impl<'al, 'adds_bnd: 'al> Drop for K<'al, 'adds_bnd> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:4:1 + --> $DIR/reject-specialized-drops-8142.rs:6:1 | -LL | struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } - | ^^^^^^^^^^^^^^^^^ +LL | struct K<'l1, 'l2> { + | ^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:28:67 + --> $DIR/reject-specialized-drops-8142.rs:63:1 | -LL | impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT - | ^^^ +LL | / impl<'al, 'adds_bnd> Drop for L<'al, 'adds_bnd> +LL | | +LL | | where +LL | | 'adds_bnd: 'al, + | |___________________^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:5:1 + --> $DIR/reject-specialized-drops-8142.rs:10:1 | -LL | struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } - | ^^^^^^^^^^^^^^^^^ +LL | struct L<'l1, 'l2> { + | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:34:1 + --> $DIR/reject-specialized-drops-8142.rs:75:1 | -LL | impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for N<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `'static` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:7:1 + --> $DIR/reject-specialized-drops-8142.rs:17:1 | -LL | struct N<'n> { x: &'n i8 } +LL | struct N<'n> { | ^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:39:1 + --> $DIR/reject-specialized-drops-8142.rs:84:1 | -LL | impl Drop for P { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for P { + | ^^^^^^^^^^^^^^^^^^^ | = note: `i8` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:9:1 + --> $DIR/reject-specialized-drops-8142.rs:23:1 | -LL | struct P { x: *const Tp } +LL | struct P { | ^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:42:14 + --> $DIR/reject-specialized-drops-8142.rs:89:15 | -LL | impl Drop for Q { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Q { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:10:1 + --> $DIR/reject-specialized-drops-8142.rs:26:1 | -LL | struct Q { x: *const Tq } - | ^^^^^^^^^^^^ - -error[E0367]: `Drop` impl requires `AddsRBnd: 'rbnd` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:45:21 - | -LL | impl<'rbnd,AddsRBnd:'rbnd> Drop for R { fn drop(&mut self) { } } // REJECT - | ^^^^^ - | -note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:11:1 - | -LL | struct R { x: *const Tr } +LL | struct Q { | ^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:54:1 + --> $DIR/reject-specialized-drops-8142.rs:110:1 | -LL | impl Drop for V { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for V { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `One` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:15:1 + --> $DIR/reject-specialized-drops-8142.rs:39:1 | -LL | struct V { x: *const Tva, y: *const Tvb } +LL | struct V { | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:57:1 + --> $DIR/reject-specialized-drops-8142.rs:115:1 | -LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl<'lw> Drop for W<'lw, 'lw> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `'lw` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:16:1 + --> $DIR/reject-specialized-drops-8142.rs:43:1 | -LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } +LL | struct W<'l1, 'l2> { | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:60:1 + --> $DIR/reject-specialized-drops-8142.rs:120:1 | -LL | impl Drop for X<3> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for X<3> { + | ^^^^^^^^^^^^^^^^^^ | = note: `3` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:17:1 + --> $DIR/reject-specialized-drops-8142.rs:47:1 | LL | struct X; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:63:1 + --> $DIR/reject-specialized-drops-8142.rs:125:1 | -LL | impl Drop for Y { fn drop(&mut self) { } } // REJECT +LL | impl Drop for Y { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `Ca` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:18:1 + --> $DIR/reject-specialized-drops-8142.rs:48:1 | LL | struct Y; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:66:14 + --> $DIR/reject-specialized-drops-8142.rs:130:15 | -LL | impl Drop for Enum { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Enum { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:20:1 + --> $DIR/reject-specialized-drops-8142.rs:50:1 | -LL | enum Enum { Variant(T) } +LL | enum Enum { | ^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:69:14 + --> $DIR/reject-specialized-drops-8142.rs:135:15 | -LL | impl Drop for TupleStruct { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for TupleStruct { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:21:1 + --> $DIR/reject-specialized-drops-8142.rs:53:1 | LL | struct TupleStruct(T); | ^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the union it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:72:21 + --> $DIR/reject-specialized-drops-8142.rs:140:22 | -LL | impl Drop for Union { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Union { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:22:1 + --> $DIR/reject-specialized-drops-8142.rs:54:1 | -LL | union Union { f: T } +LL | union Union { | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0366, E0367. For more information about an error, try `rustc --explain E0366`. diff --git a/tests/ui/dropck/transitive-outlives.bad.stderr b/tests/ui/dropck/transitive-outlives.bad.stderr index 9ecc4841dce53..5b7968fce80c1 100644 --- a/tests/ui/dropck/transitive-outlives.bad.stderr +++ b/tests/ui/dropck/transitive-outlives.bad.stderr @@ -1,8 +1,11 @@ error[E0367]: `Drop` impl requires `'a: 'c` but the struct it is implemented for does not - --> $DIR/transitive-outlives.rs:20:9 + --> $DIR/transitive-outlives.rs:18:1 | -LL | 'a: 'c, - | ^^ +LL | / impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +LL | | +LL | | where +LL | | 'a: 'c, + | |___________^ | note: the implementor must specify the same requirement --> $DIR/transitive-outlives.rs:7:1 diff --git a/tests/ui/dropck/transitive-outlives.rs b/tests/ui/dropck/transitive-outlives.rs index e96ac6faae478..37c0a1ff5e271 100644 --- a/tests/ui/dropck/transitive-outlives.rs +++ b/tests/ui/dropck/transitive-outlives.rs @@ -16,9 +16,9 @@ where #[cfg(bad)] impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +//[bad]~^ ERROR `Drop` impl requires `'a: 'c` where 'a: 'c, - //[bad]~^ ERROR `Drop` impl requires `'a: 'c` { fn drop(&mut self) {} } diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.rs b/tests/ui/dropck/unconstrained_const_param_on_drop.rs index 369ec7280bd36..de77fa55fb280 100644 --- a/tests/ui/dropck/unconstrained_const_param_on_drop.rs +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.rs @@ -1,8 +1,7 @@ -//@ known-bug: unknown -//@ failure-status: 101 - struct Foo {} impl Drop for Foo {} +//~^ ERROR: `Drop` impl requires `the constant `_` has type `usize`` +//~| ERROR: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates fn main() {} diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr index b48ae8cc8af21..851888534eeb2 100644 --- a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr @@ -1,212 +1,17 @@ -thread 'rustc' panicked at compiler/rustc_middle/src/ty/sty.rs:360:36: -called `Option::unwrap()` on a `None` value -stack backtrace: - 0: begin_panic_handler - at ./library/std/src/panicking.rs:661:5 - 1: panic_fmt - at ./library/core/src/panicking.rs:74:14 - 2: panic - at ./library/core/src/panicking.rs:148:5 - 3: core::option::unwrap_failed - at ./library/core/src/option.rs:2013:5 - 4: unwrap - at ./library/core/src/option.rs:963:21 - 5: find_ty_from_env - at ./compiler/rustc_middle/src/ty/sty.rs:360:36 - 6: process_obligation - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:472:29 - 7: process_obligations - at ./compiler/rustc_data_structures/src/obligation_forest/mod.rs:462:23 - 8: select - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:107:13 - 9: select_where_possible - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:160:9 - 10: select_all_or_error, rustc_trait_selection::traits::FulfillmentError> - at ./compiler/rustc_infer/src/traits/engine.rs:82:22 - 11: select_all_or_error - at ./compiler/rustc_trait_selection/src/traits/engine.rs:189:9 - 12: ensure_drop_predicates_are_implied_by_item_defn - at ./compiler/rustc_hir_analysis/src/check/dropck.rs:154:18 - 13: check_drop_impl - at ./compiler/rustc_hir_analysis/src/check/dropck.rs:60:13 - 14: call core::result::Result<(), rustc_span::ErrorGuaranteed>, (rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)> - at ./library/core/src/ops/function.rs:79:5 - 15: {closure#0} core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/util.rs:360:16 - 16: for_each_relevant_impl core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./compiler/rustc_middle/src/ty/trait_def.rs:166:21 - 17: calculate_dtor core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/util.rs:359:9 - 18: rustc_hir_analysis::check::adt_destructor - at ./compiler/rustc_hir_analysis/src/check/mod.rs:125:5 - 19: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:285:13 - [... omitted 22 frames ...] - 20: query_get_at>> - at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 - 21: adt_destructor - at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 - 22: adt_destructor - at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 - 23: destructor - at ./compiler/rustc_middle/src/ty/adt.rs:610:13 - 24: check_struct - at ./compiler/rustc_hir_analysis/src/check/check.rs:71:5 - 25: check_item_type - at ./compiler/rustc_hir_analysis/src/check/check.rs:730:13 - 26: check_item - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:335:5 - 27: check_well_formed - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:192:39 - 28: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 29: query_ensure_error_guaranteed>, ()> - at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 - 30: check_well_formed - at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 - 31: {closure#1} - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1995:47 - 32: {closure#0} - at ./compiler/rustc_middle/src/hir/mod.rs:89:57 - 33: {closure#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:203:50 - 34: call_once, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}>> - at ./library/core/src/panic/unwind_safe.rs:272:9 - 35: do_call>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/panicking.rs:553:40 - 36: try, core::panic::unwind_safe::AssertUnwindSafe>>> - at ./library/std/src/panicking.rs:517:19 - 37: catch_unwind>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/panic.rs:350:14 - 38: run, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#2}::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 - 39: {closure#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:46 - 40: {closure#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/core/src/iter/adapters/filter_map.rs:39:28 - 41: fold, core::iter::adapters::filter_map::filter_map_fold::{closure_env#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./library/core/src/slice/iter/macros.rs:232:27 - 42: fold, core::slice::iter::Iter, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/core/src/iter/adapters/filter_map.rs:148:9 - 43: {closure#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:73 - 44: parallel_guard, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 - 45: try_par_for_each_in<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:199:9 - 46: par_items - at ./compiler/rustc_middle/src/hir/mod.rs:75:9 - 47: check_mod_type_wf - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1994:19 - 48: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 49: query_ensure_error_guaranteed>, ()> - at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 - 50: check_mod_type_wf - at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 - 51: {closure#0} - at ./compiler/rustc_hir_analysis/src/lib.rs:162:21 - 52: {closure#0} - at ./compiler/rustc_middle/src/hir/map/mod.rs:463:13 - 53: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:182:34 - 54: call_once<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./library/core/src/panic/unwind_safe.rs:272:9 - 55: do_call>>, ()> - at ./library/std/src/panicking.rs:553:40 - 56: try<(), core::panic::unwind_safe::AssertUnwindSafe>>> - at ./library/std/src/panicking.rs:517:19 - 57: catch_unwind>>, ()> - at ./library/std/src/panic.rs:350:14 - 58: run<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#1}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 - 59: {closure#1}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:186:21 - 60: for_each>> - at ./library/core/src/slice/iter/macros.rs:254:21 - 61: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:185:17 - 62: parallel_guard<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 - 63: par_for_each_in<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:178:9 - 64: par_for_each_module - at ./compiler/rustc_middle/src/hir/map/mod.rs:462:9 - 65: {closure#0} - at ./compiler/rustc_hir_analysis/src/lib.rs:161:9 - 66: run<(), rustc_hir_analysis::check_crate::{closure_env#0}> - at ./compiler/rustc_data_structures/src/profiling.rs:754:9 - 67: time<(), rustc_hir_analysis::check_crate::{closure_env#0}> - at ./compiler/rustc_session/src/utils.rs:16:9 - 68: check_crate - at ./compiler/rustc_hir_analysis/src/lib.rs:160:5 - 69: run_required_analyses - at ./compiler/rustc_interface/src/passes.rs:784:5 - 70: analysis - at ./compiler/rustc_interface/src/passes.rs:823:5 - 71: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 72: query_get_at>> - at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 - 73: analysis - at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 - 74: analysis - at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 - 75: {closure#5} - at ./compiler/rustc_driver_impl/src/lib.rs:445:52 - 76: {closure#1}> - at ./compiler/rustc_middle/src/ty/context.rs:1296:37 - 77: {closure#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/context/tls.rs:82:9 - 78: try_with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/thread/local.rs:283:12 - 79: with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/thread/local.rs:260:9 - 80: enter_context>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/context/tls.rs:79:9 - 81: enter> - at ./compiler/rustc_middle/src/ty/context.rs:1296:9 - 82: {closure#1} - at ./compiler/rustc_driver_impl/src/lib.rs:445:13 - 83: enter, rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/queries.rs:202:19 - 84: {closure#0} - at ./compiler/rustc_driver_impl/src/lib.rs:389:22 - 85: {closure#1}, rustc_driver_impl::run_compiler::{closure_env#0}> - at ./compiler/rustc_interface/src/interface.rs:502:27 - 86: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:154:13 - 87: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:106:21 - 88: set, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at /home/boxy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/scoped-tls-1.0.1/src/lib.rs:137:9 - 89: create_session_globals_then, rustc_interface::util::run_in_thread_with_globals::{closure#0}::{closure#0}::{closure_env#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./compiler/rustc_span/src/lib.rs:134:5 - 90: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:105:17 -note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. - -error: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc 1.81.0-dev running on x86_64-unknown-linux-gnu - -note: compiler flags: -Z threads=1 -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ignore-directory-in-diagnostics-source-blocks=/home/boxy/.cargo -Z ignore-directory-in-diagnostics-source-blocks=/media/Nyoomies/Repos/rust/vendor -C codegen-units=1 -Z ui-testing -Z deduplicate-diagnostics=no -Z write-long-types-to-disk=no -C strip=debuginfo -C prefer-dynamic -C rpath -C debuginfo=0 +error[E0367]: `Drop` impl requires `the constant `_` has type `usize`` but the struct it is implemented for does not + --> $DIR/unconstrained_const_param_on_drop.rs:3:6 + | +LL | impl Drop for Foo {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/unconstrained_const_param_on_drop.rs:1:1 + | +LL | struct Foo {} + | ^^^^^^^^^^ -query stack during panic: -#0 [adt_destructor] computing `Drop` impl for `Foo` -#1 [check_well_formed] checking that `Foo` is well-formed -#2 [check_mod_type_wf] checking that types are well-formed in top-level module -#3 [analysis] running analysis passes on this crate -end of query stack error[E0207]: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained_const_param_on_drop.rs:6:6 + --> $DIR/unconstrained_const_param_on_drop.rs:3:6 | LL | impl Drop for Foo {} | ^^^^^^^^^^^^^^^^^^^ unconstrained const parameter @@ -214,6 +19,7 @@ LL | impl Drop for Foo {} = note: expressions using a const parameter must map each value to a distinct output value = note: proving the result of expressions other than the parameter are unique is not supported -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0207`. +Some errors have detailed explanations: E0207, E0367. +For more information about an error, try `rustc --explain E0207`. From 91af6b51223625ea5cd34d77861648ca19566deb Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 2 Jul 2024 15:00:09 -0700 Subject: [PATCH 010/806] Add edge-case examples to `{count,leading,trailing}_{ones,zeros}` methods Some architectures (i386) do not define a "count leading zeros" instruction, they define a "find first set bit" instruction (`bsf`) whose result is undefined when given zero (ie none of the bits are set). Of this family of bitwise operations, I always forget which of these things is potentially undefined for zero, and I'm also not 100% sure that Rust provides a hard guarantee for the results of these methods when given zero. So I figured there are others who have these same uncertainties, and it would be good to resolve them and answer the question via extending these doc examples/tests. See https://en.wikipedia.org/wiki/Find_first_set#Hardware_support for more info on i386 and `bsf` on zero. --- library/core/src/num/uint_macros.rs | 41 ++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ad72c29758bd7..40bc58204e808 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -65,8 +65,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b01001100", stringify!($SelfT), ";")] - /// /// assert_eq!(n.count_ones(), 3); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.count_ones(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.count_ones(), 0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -86,7 +91,11 @@ macro_rules! uint_impl { /// Basic usage: /// /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);")] + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.count_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + /// assert_eq!(max.count_zeros(), 0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -108,8 +117,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = ", stringify!($SelfT), "::MAX >> 2;")] - /// /// assert_eq!(n.leading_zeros(), 2); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.leading_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + /// assert_eq!(max.leading_zeros(), 0); /// ``` #[doc = concat!("[`ilog2`]: ", stringify!($SelfT), "::ilog2")] #[stable(feature = "rust1", since = "1.0.0")] @@ -130,8 +144,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b0101000", stringify!($SelfT), ";")] - /// /// assert_eq!(n.trailing_zeros(), 3); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.trailing_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.trailing_zeros(), 0);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -150,8 +169,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = !(", stringify!($SelfT), "::MAX >> 2);")] - /// /// assert_eq!(n.leading_ones(), 2); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.leading_ones(), 0); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.leading_ones(), ", stringify!($BITS), ");")] /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] @@ -171,8 +195,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b1010111", stringify!($SelfT), ";")] - /// /// assert_eq!(n.trailing_ones(), 3); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.trailing_ones(), 0); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.trailing_ones(), ", stringify!($BITS), ");")] /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] From c90b6b8d2937cc116c635aa5c58f90afcc535248 Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 9 May 2024 15:41:15 +0200 Subject: [PATCH 011/806] stabilize `const_int_from_str` --- library/core/src/lib.rs | 1 - library/core/src/num/error.rs | 2 +- library/core/src/num/mod.rs | 6 ++++-- library/core/tests/lib.rs | 1 - tests/ui/consts/const-eval/parse_ints.rs | 2 -- tests/ui/consts/const-eval/parse_ints.stderr | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c5a1fca667bc3..ef90333596fdb 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -128,7 +128,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index a2d7e6f7b0754..b8e22a8aef955 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,7 +113,7 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "int_error_matching", since = "1.55.0")] pub const fn kind(&self) -> &IntErrorKind { &self.kind diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 034af6a0d5731..6010f7cee06eb 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,6 +1387,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] +#[rustc_const_unstable(issue = "none", feature = "const_int_cannot_overflow")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1410,6 +1411,7 @@ const fn from_str_radix_panic(radix: u32) { intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } +#[allow_internal_unstable(const_int_cannot_overflow)] macro_rules! from_str_radix { ($($int_ty:ty)+) => {$( impl $int_ty { @@ -1436,7 +1438,7 @@ macro_rules! from_str_radix { #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -1566,7 +1568,7 @@ macro_rules! from_str_radix_size_impl { #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { match <$t>::from_str_radix(src, radix) { Ok(x) => Ok(x as $size), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 83a615fcd8be3..feef5e81480c4 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -16,7 +16,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] -#![feature(const_int_from_str)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_pointer_is_aligned)] diff --git a/tests/ui/consts/const-eval/parse_ints.rs b/tests/ui/consts/const-eval/parse_ints.rs index ff9fc47e65c33..cb9a3eb431299 100644 --- a/tests/ui/consts/const-eval/parse_ints.rs +++ b/tests/ui/consts/const-eval/parse_ints.rs @@ -1,5 +1,3 @@ -#![feature(const_int_from_str)] - const _OK: () = match i32::from_str_radix("-1234", 10) { Ok(x) => assert!(x == -1234), Err(_) => panic!(), diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index 9e49fe433a126..ec9249ece8e39 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -6,7 +6,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_LOW` - --> $DIR/parse_ints.rs:7:24 + --> $DIR/parse_ints.rs:5:24 | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_HIGH` - --> $DIR/parse_ints.rs:8:25 + --> $DIR/parse_ints.rs:6:25 | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From eb799cf634a811d1e0d719d30cba83d5611f87c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 20:51:50 +0200 Subject: [PATCH 012/806] mark `can_not_overflow` as `#[rustc_const_stable(...)]` see https://github.com/rust-lang/rust/pull/124941#discussion_r1664676739 --- library/core/src/num/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6010f7cee06eb..0522365e22e0a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,7 +1387,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] -#[rustc_const_unstable(issue = "none", feature = "const_int_cannot_overflow")] +#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1411,7 +1411,6 @@ const fn from_str_radix_panic(radix: u32) { intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } -#[allow_internal_unstable(const_int_cannot_overflow)] macro_rules! from_str_radix { ($($int_ty:ty)+) => {$( impl $int_ty { From f99df29d9dcc1c313593967c8c390fae39d462d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 21:23:55 +0200 Subject: [PATCH 013/806] fix tests after rebase --- src/tools/clippy/tests/ui/from_str_radix_10.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs index 2d5b351f8da3e..0df6a0a202ae7 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.rs +++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); From 404519a6e5a2e95d0b8f057e35a7d6280fb9c562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 22:31:53 +0200 Subject: [PATCH 014/806] bless tests --- .../clippy/tests/ui/from_str_radix_10.fixed | 4 ++-- .../clippy/tests/ui/from_str_radix_10.stderr | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed index f9ce1defda17c..6c582190b4424 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed +++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 01a1bf8940a12..4aa84eca26120 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:29:5 + --> tests/ui/from_str_radix_10.rs:28:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:32:5 + --> tests/ui/from_str_radix_10.rs:31:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:34:5 + --> tests/ui/from_str_radix_10.rs:33:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:36:5 + --> tests/ui/from_str_radix_10.rs:35:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:38:5 + --> tests/ui/from_str_radix_10.rs:37:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:40:5 + --> tests/ui/from_str_radix_10.rs:39:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:44:5 + --> tests/ui/from_str_radix_10.rs:43:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:48:5 + --> tests/ui/from_str_radix_10.rs:47:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` From dd9e8c835afffc67f0d694abb78bf8b40f87aa97 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Jun 2024 18:50:07 -0400 Subject: [PATCH 015/806] Refactor `arc_with_non_send_sync` * Check HIR beffore type checking * Only lint when `Arc::new` is called --- clippy_lints/src/arc_with_non_send_sync.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index d57ab539fff42..4eafa330fafab 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_from_proc_macro, last_path_segment}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -42,12 +42,11 @@ declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() - && let ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, ty, sym::Arc) - && let ExprKind::Call(func, [arg]) = expr.kind - && let ExprKind::Path(func_path) = func.kind - && last_path_segment(&func_path).ident.name == sym::new + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind + && func_name.ident.name == sym::new + && !expr.span.from_expansion() + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { From 01f53c2c160a6efe3080a2fe01b87c5732f860d1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Jun 2024 22:15:01 -0400 Subject: [PATCH 016/806] Refactor `borrow_deref_ref`: Match HIR tree before checking for macros. --- clippy_lints/src/borrow_deref_ref.rs | 44 +++++++++++++--------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 0ca4a0e067d36..bd123a725a736 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -49,35 +49,31 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if !e.span.from_expansion() - && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind - && !addrof_target.span.from_expansion() + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind - && !deref_target.span.from_expansion() && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) + && !e.span.from_expansion() + && !deref_target.span.from_expansion() + && !addrof_target.span.from_expansion() && let ref_ty = cx.typeck_results().expr_ty(deref_target) && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() - { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) - && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) - { - return; + && get_parent_expr(cx, e).map_or(true, |parent| { + match parent.kind { + // `*&*foo` should lint `deref_addrof` instead. + ExprKind::Unary(UnOp::Deref, _) => is_lint_allowed(cx, DEREF_ADDROF, parent.hir_id), + // `&*foo` creates a distinct temporary from `foo` + ExprKind::AddrOf(_, Mutability::Mut, _) => !matches!( + deref_target.kind, + ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..) + ), + _ => true, } - - // modification to `&mut &*x` is different from `&mut x` - if matches!( - deref_target.kind, - ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..) - ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) - { - return; - } - } - if is_from_proc_macro(cx, e) { - return; - } - + }) + && !is_from_proc_macro(cx, e) + { span_lint_and_then( cx, BORROW_DEREF_REF, From 39cd58ee7557cd0d45b547e9a57fee711fe0e3a0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Jun 2024 23:14:44 -0400 Subject: [PATCH 017/806] Refactor `collapsible_if`: Check AST before checking for macros --- clippy_lints/src/collapsible_if.rs | 33 +++++++++++------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 07b02c98df151..f311c052ad6ee 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -93,20 +93,14 @@ declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { - check_if(cx, expr); - } - } -} - -fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::If(check, then, else_) = &expr.kind { - if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, then.span, else_); - } else if let ast::ExprKind::Let(..) = check.kind { - // Prevent triggering on `if let a = b { if c { .. } }`. - } else { - check_collapsible_no_if_let(cx, expr, check, then); + if let ast::ExprKind::If(cond, then, else_) = &expr.kind + && !expr.span.from_expansion() + { + if let Some(else_) = else_ { + check_collapsible_maybe_if_let(cx, then.span, else_); + } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { + check_collapsible_no_if_let(cx, expr, cond, then); + } } } } @@ -189,13 +183,10 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & /// If the block contains only one expression, return it. fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - let mut it = block.stmts.iter(); - - if let (Some(stmt), None) = (it.next(), it.next()) { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr), - _ => None, - } + if let [stmt] = &*block.stmts + && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind + { + Some(expr) } else { None } From 1f530ab7e2bd74efdfc5c0ac7f1c0d9f8df8c029 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Jun 2024 23:56:24 -0400 Subject: [PATCH 018/806] Refactor `collection_is_never_read`: Check HIR tree before checking types. --- clippy_lints/src/collection_is_never_read.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 28d9f68d504cc..eebda3ff76fd9 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -59,9 +59,9 @@ static COLLECTIONS: [Symbol; 9] = [ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { - // Look for local variables whose type is a container. Search surrounding bock for read access. - if match_acceptable_type(cx, local, &COLLECTIONS) - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + // Look for local variables whose type is a container. Search surrounding block for read access. + if let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && match_acceptable_type(cx, local, &COLLECTIONS) && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) && has_no_read_access(cx, local_id, enclosing_block) { From d15ec0ff852f8e1befc34e83dc2865b78f506898 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 9 Jun 2024 00:03:20 -0400 Subject: [PATCH 019/806] Refactor `crate_in_macro_def`: * Don't clone the token stream * Check the HIR tree before reading attributes --- clippy_lints/src/crate_in_macro_def.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index adf6f7c473757..678bdbc0ffb8a 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -53,10 +53,9 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.attrs.iter().any(is_macro_export) - && let ItemKind::MacroDef(macro_def) = &item.kind - && let tts = macro_def.body.tokens.clone() - && let Some(span) = contains_unhygienic_crate_reference(&tts) + if let ItemKind::MacroDef(macro_def) = &item.kind + && item.attrs.iter().any(is_macro_export) + && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { span_lint_and_sugg( cx, From 441500b55b4529e8ba231c8b7165f8686f32e67c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 9 Jun 2024 22:29:11 -0400 Subject: [PATCH 020/806] Refactor `else_if_without_else`: Check the AST before the macro check. --- clippy_lints/src/else_if_without_else.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index bb6f9aac22363..7a6dc46972767 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -50,12 +50,9 @@ declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if in_external_macro(cx.sess(), item.span) { - return; - } - if let ExprKind::If(_, _, Some(ref els)) = item.kind && let ExprKind::If(_, _, None) = els.kind + && !in_external_macro(cx.sess(), item.span) { span_lint_and_help( cx, From d1dcd918e72e62bbf81c01c1b4cebaf1d9a4eb6f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 10 Jun 2024 01:40:06 -0400 Subject: [PATCH 021/806] `equatable_if_let`: Check HIR tree before checking for macros. --- clippy_lints/src/equatable_if_let.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 37442bf3e2867..fb9f2b1526e38 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -70,9 +70,9 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Let(let_expr) = expr.kind + if let ExprKind::Let(let_expr) = expr.kind && unary_pattern(let_expr.pat) + && !in_external_macro(cx.sess(), expr.span) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); From 672b8b512a7c5bd6c30091647a637cfede97f8aa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 9 Jun 2024 22:32:55 -0400 Subject: [PATCH 022/806] Refactor `empty_enums`: Check the HIR tree before the feature check. --- clippy_lints/src/empty_enum.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index d16714695cb7e..1869faab1d326 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -64,25 +64,21 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - // Only suggest the `never_type` if the feature is enabled - if !cx.tcx.features().never_type { - return; - } - - if let ItemKind::Enum(..) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - if adt.variants().is_empty() { - span_lint_and_help( - cx, - EMPTY_ENUM, - item.span, - "enum with no variants", - None, - "consider using the uninhabited type `!` (never type) or a wrapper \ - around it to introduce a type which can't be instantiated", - ); - } + if let ItemKind::Enum(..) = item.kind + // Only suggest the `never_type` if the feature is enabled + && cx.tcx.features().never_type + && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def() + && adt.variants().is_empty() + { + span_lint_and_help( + cx, + EMPTY_ENUM, + item.span, + "enum with no variants", + None, + "consider using the uninhabited type `!` (never type) or a wrapper \ + around it to introduce a type which can't be instantiated", + ); } } } From 5b0dac6fe2fb574a6828d0a8d923e542ffd83d74 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 10 Jun 2024 02:26:28 -0400 Subject: [PATCH 023/806] `error_impl_error`: Get `Error` `DefId` after checking the HIR tree. --- clippy_lints/src/error_impl_error.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 8e49138cd26ba..1e6447dc25370 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -36,15 +36,12 @@ declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { - return; - }; - match item.kind { ItemKind::TyAlias(..) if item.ident.name == sym::Error && is_visible_outside_module(cx, item.owner_id.def_id) && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( @@ -56,9 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }, ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) - && let hir_id = cx.tcx.local_def_id_to_hir_id(def_id) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => @@ -66,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { span_lint_hir_and_then( cx, ERROR_IMPL_ERROR, - hir_id, + cx.tcx.local_def_id_to_hir_id(def_id), ident.span, "exported type named `Error` that implements `Error`", |diag| { From 53688950775bfa2c986dc085fefdf1ba8ffe0932 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 00:39:51 -0400 Subject: [PATCH 024/806] `exhaustive_items`: Don't check the item kind twice. --- clippy_lints/src/exhaustive_items.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 436dc8611bdeb..0f4176ec73bb2 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -70,20 +70,24 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind - && cx.effective_visibilities.is_exported(item.owner_id.def_id) + let (lint, msg, fields) = match item.kind { + ItemKind::Enum(..) => ( + EXHAUSTIVE_ENUMS, + "exported enums should not be exhaustive", + [].as_slice(), + ), + ItemKind::Struct(v, ..) => ( + EXHAUSTIVE_STRUCTS, + "exported structs should not be exhaustive", + v.fields(), + ), + _ => return, + }; + if cx.effective_visibilities.is_exported(item.owner_id.def_id) && let attrs = cx.tcx.hir().attrs(item.hir_id()) && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) { - // skip structs with private fields - return; - } - (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") - } else { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - }; let suggestion_span = item.span.shrink_to_lo(); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { From ca7e5c135327f3dadf5611e146400186216cdb85 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 00:43:58 -0400 Subject: [PATCH 025/806] `exit`: Use `OwnerNode`. --- clippy_lints/src/exit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 91c94d66458dc..f37d11f7eb9b7 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -47,8 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) - && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id - && let Node::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_node_by_def_id(parent) + && let parent = cx.tcx.hir().get_parent_item(e.hir_id) + && let OwnerNode::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_owner_node(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning && !is_entrypoint_fn(cx, parent.to_def_id()) From 278e8371e64e09b684127054a78b0166074b5e9b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 13:30:26 -0400 Subject: [PATCH 026/806] `float_literal`: Check HIR tree before checking types. --- clippy_lints/src/float_literal.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 2261fcdbdabc9..6adcd2235dc52 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -62,10 +62,9 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Float(fty) = *ty.kind() - && let hir::ExprKind::Lit(lit) = expr.kind + if let hir::ExprKind::Lit(lit) = expr.kind && let LitKind::Float(sym, lit_float_ty) = lit.node + && let ty::Float(fty) = *cx.typeck_results().expr_ty(expr).kind() { let sym_str = sym.as_str(); let formatter = FloatFormat::new(sym_str); From 3fcac2cba8ec697a8377336e765d704d710c8e60 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:10:10 -0400 Subject: [PATCH 027/806] `from_over_into`: Check HIR tree first. --- clippy_lints/src/from_over_into.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 93527bcdf5cee..618530136e201 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -64,10 +64,6 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { - return; - } - if let ItemKind::Impl(Impl { of_trait: Some(hir_trait_ref), self_ty, @@ -77,6 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) + && span_is_local(item.span) && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) .map(ty::EarlyBinder::instantiate_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) From 9f59e9a777e4807374aaf0254c04c409850826fb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:17:38 -0400 Subject: [PATCH 028/806] `from_str_radix_10` : Reorder checks --- clippy_lints/src/from_str_radix_10.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 82ce501bac59b..9acb72b2e3729 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -47,9 +47,13 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind - // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + + // check if the second argument is a primitive `10` + && is_integer_literal(radix, 10) + + // check if the second part of the path indeed calls the associated + // function `from_str_radix` + && pathseg.ident.name.as_str() == "from_str_radix" // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind @@ -57,12 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && let def::Res::PrimTy(prim_ty) = ty_res && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) - // check if the second part of the path indeed calls the associated - // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" - - // check if the second argument is a primitive `10` - && is_integer_literal(radix, 10) + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); From 65b9fae5655f4cfe989efa685700e89ecfcc77f9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Jun 2024 23:45:59 -0400 Subject: [PATCH 029/806] Refactor `checked_conversions`: * Check HIR tree before checking macros, msrv and constness * Remove redundant HIR tree matching --- clippy_lints/src/checked_conversions.rs | 133 +++++++++++------------- 1 file changed, 59 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 92810ea2aa0ff..1ec4b831a2c48 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -50,61 +50,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !self.msrv.meets(msrvs::TRY_FROM) { - return; - } - - let result = if !in_constant(cx, item.hir_id) - && !in_external_macro(cx.sess(), item.span) - && let ExprKind::Binary(op, left, right) = &item.kind - { - match op.node { - BinOpKind::Ge | BinOpKind::Le => single_check(item), - BinOpKind::And => double_check(cx, left, right), - _ => None, + if let ExprKind::Binary(op, lhs, rhs) = item.kind + && let (lt1, gt1, op2) = match op.node { + BinOpKind::Le => (lhs, rhs, None), + BinOpKind::Ge => (rhs, lhs, None), + BinOpKind::And + if let ExprKind::Binary(op1, lhs1, rhs1) = lhs.kind + && let ExprKind::Binary(op2, lhs2, rhs2) = rhs.kind + && let Some((lt1, gt1)) = read_le_ge(op1.node, lhs1, rhs1) + && let Some((lt2, gt2)) = read_le_ge(op2.node, lhs2, rhs2) => + { + (lt1, gt1, Some((lt2, gt2))) + }, + _ => return, } - } else { - None - }; - - if let Some(cv) = result { - if let Some(to_type) = cv.to_type { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - CHECKED_CONVERSIONS, - item.span, - "checked cast can be simplified", - "try", - format!("{to_type}::try_from({snippet}).is_ok()"), - applicability, - ); + && !in_external_macro(cx.sess(), item.span) + && !in_constant(cx, item.hir_id) + && self.msrv.meets(msrvs::TRY_FROM) + && let Some(cv) = match op2 { + // todo: check for case signed -> larger unsigned == only x >= 0 + None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned), + Some((lt2, gt2)) => { + let upper_lower = |lt1, gt1, lt2, gt2| { + check_upper_bound(lt1, gt1) + .zip(check_lower_bound(lt2, gt2)) + .and_then(|(l, r)| l.combine(r, cx)) + }; + upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1)) + }, } + && let Some(to_type) = cv.to_type + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + CHECKED_CONVERSIONS, + item.span, + "checked cast can be simplified", + "try", + format!("{to_type}::try_from({snippet}).is_ok()"), + applicability, + ); } } extract_msrv_attr!(LateContext); } -/// Searches for a single check from unsigned to _ is done -/// todo: check for case signed -> larger unsigned == only x >= 0 -fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { - check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned) -} - -/// Searches for a combination of upper & lower bound checks -fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option> { - let upper_lower = |l, r| { - let upper = check_upper_bound(l); - let lower = check_lower_bound(r); - - upper.zip(lower).and_then(|(l, r)| l.combine(r, cx)) - }; - - upper_lower(left, right).or_else(|| upper_lower(right, left)) -} - /// Contains the result of a tried conversion check #[derive(Clone, Debug)] struct Conversion<'a> { @@ -121,6 +114,19 @@ enum ConversionType { FromUnsigned, } +/// Attempts to read either `<=` or `>=` with a normalized operand order. +fn read_le_ge<'tcx>( + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + match op { + BinOpKind::Le => Some((lhs, rhs)), + BinOpKind::Ge => Some((rhs, lhs)), + _ => None, + } +} + impl<'a> Conversion<'a> { /// Combine multiple conversions if the are compatible pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option> { @@ -188,29 +194,17 @@ impl ConversionType { } /// Check for `expr <= (to_type::MAX as from_type)` -fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { - if let ExprKind::Binary(ref op, left, right) = &expr.kind - && let Some((candidate, check)) = normalize_le_ge(op, left, right) - && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX") - { - Conversion::try_new(candidate, from, to) +fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option> { + if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") { + Conversion::try_new(lt, from, to) } else { None } } /// Check for `expr >= 0|(to_type::MIN as from_type)` -fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { - fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option> { - (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check))) - } - - // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, left, right) = &expr.kind { - normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) - } else { - None - } +fn check_lower_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option> { + check_lower_bound_zero(gt, lt).or_else(|| check_lower_bound_min(gt, lt)) } /// Check for `expr >= 0` @@ -309,15 +303,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { } } -/// Will return the expressions as if they were expr1 <= expr2 -fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { - match op.node { - BinOpKind::Le => Some((left, right)), - BinOpKind::Ge => Some((right, left)), - _ => None, - } -} - // Constants const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; From 0a25df87efd5fdf1543d26764c929d8d6e00a48a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 9 Jun 2024 22:27:39 -0400 Subject: [PATCH 030/806] Refactor `double_parens`: * Merge control flow before emission * Check the AST before the macro check --- clippy_lints/src/double_parens.rs | 48 ++++++++++++++----------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index b51bb7951b741..4dd8f01ee7090 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -40,35 +40,29 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.from_expansion() { - return; - } - - let msg: &str = "consider removing unnecessary double parentheses"; - - match expr.kind { - ExprKind::Paren(ref in_paren) => match in_paren.kind { - ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, msg); - }, - _ => {}, - }, - ExprKind::Call(_, ref params) => { - if params.len() == 1 { - let param = ¶ms[0]; - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); - } - } + let span = match &expr.kind { + ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, + ExprKind::Call(_, params) + if let [param] = &**params + && let ExprKind::Paren(_) = param.kind => + { + param.span }, - ExprKind::MethodCall(ref call) => { - if let [ref arg] = call.args[..] { - if let ExprKind::Paren(_) = arg.kind { - span_lint(cx, DOUBLE_PARENS, arg.span, msg); - } - } + ExprKind::MethodCall(call) + if let [arg] = &*call.args + && let ExprKind::Paren(_) = arg.kind => + { + arg.span }, - _ => {}, + _ => return, + }; + if !expr.span.from_expansion() { + span_lint( + cx, + DOUBLE_PARENS, + span, + "consider removing unnecessary double parentheses", + ); } } } From e864519fbcaef5141e8fda7fdfac1a2c8d363cd2 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 7 Jul 2024 08:11:32 +0700 Subject: [PATCH 031/806] Add test for manual_unwrap_or in issue 13018 --- tests/ui/manual_unwrap_or.fixed | 9 +++++++++ tests/ui/manual_unwrap_or.rs | 13 +++++++++++++ tests/ui/manual_unwrap_or.stderr | 12 +++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 74afa00e12f27..2899c3518de80 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -234,4 +234,13 @@ fn implicit_deref_ref() { }; } +mod issue_13018 { + use std::collections::HashMap; + + type RefName = i32; + pub fn get(index: &HashMap>, id: usize) -> &[RefName] { + index.get(&id).unwrap_or(&[]) + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 2d01b8ceaaa75..e2c04b01ed962 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -284,4 +284,17 @@ fn implicit_deref_ref() { }; } +mod issue_13018 { + use std::collections::HashMap; + + type RefName = i32; + pub fn get(index: &HashMap>, id: usize) -> &[RefName] { + if let Some(names) = index.get(&id) { + names + } else { + &[] + } + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index c93a8952a0805..28b59df161dfe 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -172,5 +172,15 @@ LL | | None => 0, LL | | }; | |_________^ help: replace with: `some_macro!().unwrap_or(0)` -error: aborting due to 16 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:292:9 + | +LL | / if let Some(names) = index.get(&id) { +LL | | names +LL | | } else { +LL | | &[] +LL | | } + | |_________^ help: replace with: `index.get(&id).unwrap_or(&[])` + +error: aborting due to 17 previous errors From c46c1f6da6725e0aa33e7738e9e0eb2f58b46eb6 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 7 Jul 2024 20:41:55 +0700 Subject: [PATCH 032/806] Fix 13018: self should be T with `Option::unwrap_or(self, T) -> T`. --- clippy_lints/src/matches/manual_unwrap_or.rs | 11 +++++++++++ tests/ui/manual_unwrap_or.fixed | 9 ++++++++- tests/ui/manual_unwrap_or.rs | 11 +++++++---- tests/ui/manual_unwrap_or.stderr | 12 +----------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 0940fc3219bb6..85a08f81c2f34 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -35,6 +35,17 @@ pub(super) fn check_if_let<'tcx>( else_expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(let_expr); + let then_ty = cx.typeck_results().expr_ty(then_expr); + // The signature is `fn unwrap_or(self: Option, default: T) -> T`. + // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. + // Otherwise, type error will occur. + if cx.typeck_results().expr_adjustments(then_expr).is_empty() + && let rustc_middle::ty::Adt(_did, args) = ty.kind() + && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) + && some_ty != then_ty + { + return; + } check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 2899c3518de80..07e4bdd483a8c 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -239,7 +239,14 @@ mod issue_13018 { type RefName = i32; pub fn get(index: &HashMap>, id: usize) -> &[RefName] { - index.get(&id).unwrap_or(&[]) + if let Some(names) = index.get(&id) { names } else { &[] } + } + + pub fn get_match(index: &HashMap>, id: usize) -> &[RefName] { + match index.get(&id) { + Some(names) => names, + None => &[], + } } } diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index e2c04b01ed962..cdbe51a41212c 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -289,10 +289,13 @@ mod issue_13018 { type RefName = i32; pub fn get(index: &HashMap>, id: usize) -> &[RefName] { - if let Some(names) = index.get(&id) { - names - } else { - &[] + if let Some(names) = index.get(&id) { names } else { &[] } + } + + pub fn get_match(index: &HashMap>, id: usize) -> &[RefName] { + match index.get(&id) { + Some(names) => names, + None => &[], } } } diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 28b59df161dfe..c93a8952a0805 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -172,15 +172,5 @@ LL | | None => 0, LL | | }; | |_________^ help: replace with: `some_macro!().unwrap_or(0)` -error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:292:9 - | -LL | / if let Some(names) = index.get(&id) { -LL | | names -LL | | } else { -LL | | &[] -LL | | } - | |_________^ help: replace with: `index.get(&id).unwrap_or(&[])` - -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors From 1b0230cf2525d8c3653cab40d888d6c9b90c059f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 10 Jun 2024 01:22:37 -0400 Subject: [PATCH 033/806] Refactor `endian_bytes`: * Merge code paths. * Check HIR tree before checking for macros. --- clippy_lints/src/endian_bytes.rs | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index bb766e9633872..f11ac5f317c0f 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -109,32 +109,27 @@ impl LintKind { impl LateLintPass<'_> for EndianBytes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind - && args.is_empty() - && let ty = cx.typeck_results().expr_ty(receiver) + let (prefix, name, ty_expr) = match expr.kind { + ExprKind::MethodCall(method_name, receiver, [], ..) => (Prefix::To, method_name.ident.name, receiver), + ExprKind::Call(function, ..) + if let ExprKind::Path(qpath) = function.kind + && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() + && let Some(function_name) = cx.get_def_path(def_id).last() => + { + (Prefix::From, *function_name, expr) + }, + _ => return, + }; + if !in_external_macro(cx.sess(), expr.span) + && let ty = cx.typeck_results().expr_ty(ty_expr) && ty.is_primitive_ty() - && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty) { - return; - } - - if let ExprKind::Call(function, ..) = expr.kind - && let ExprKind::Path(qpath) = function.kind - && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() - && let Some(function_name) = cx.get_def_path(def_id).last() - && let ty = cx.typeck_results().expr_ty(expr) - && ty.is_primitive_ty() - { - maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); + maybe_lint_endian_bytes(cx, expr, prefix, name, ty); } } } -fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool { +fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) { let ne = LintKind::Host.as_name(prefix); let le = LintKind::Little.as_name(prefix); let be = LintKind::Big.as_name(prefix); @@ -143,7 +138,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]), name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]), name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]), - _ => return false, + _ => return, }; let mut help = None; @@ -208,6 +203,4 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix } }, ); - - true } From f178316ae24453cf85d3667fed164895d260a1aa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 00:25:17 -0400 Subject: [PATCH 034/806] Refactor `excessive_bools`: * Check HIR tree before checking for macros. * Check item count before checking for bools. --- clippy_lints/src/excessive_bools.rs | 95 +++++++++++++---------------- 1 file changed, 43 insertions(+), 52 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 62d5ce24d40a6..76e72eefa180e 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -87,12 +87,6 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -enum Kind { - Struct, - Fn, -} - impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -101,55 +95,50 @@ impl ExcessiveBools { max_fn_params_bools, } } +} - fn too_many_bools<'tcx>(&self, tys: impl Iterator>, kind: Kind) -> bool { - if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { - (if Kind::Fn == kind { - self.max_fn_params_bools - } else { - self.max_struct_bools - }) < bools - } else { - false - } - } +impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); - fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { - if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { - span_lint_and_help( - cx, - FN_PARAMS_EXCESSIVE_BOOLS, - span, - format!("more than {} bools in function parameters", self.max_fn_params_bools), - None, - "consider refactoring bools into two-variant enums", - ); - } - } +fn has_n_bools<'tcx>(iter: impl Iterator>, mut count: u64) -> bool { + iter.filter(|ty| is_bool(ty)).any(|_| { + let (x, overflow) = count.overflowing_sub(1); + count = x; + overflow + }) } -impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); +fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) { + if has_n_bools(decl.inputs.iter(), max) && !sp.from_expansion() { + span_lint_and_help( + cx, + FN_PARAMS_EXCESSIVE_BOOLS, + sp, + format!("more than {max} bools in function parameters"), + None, + "consider refactoring bools into two-variant enums", + ); + } +} impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if item.span.from_expansion() { - return; - } - if let ItemKind::Struct(variant_data, _) = &item.kind { - if has_repr_attr(cx, item.hir_id()) { - return; - } - - if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } + if let ItemKind::Struct(variant_data, _) = &item.kind + && variant_data.fields().len() as u64 > self.max_struct_bools + && has_n_bools( + variant_data.fields().iter().map(|field| field.ty), + self.max_struct_bools, + ) + && !has_repr_attr(cx, item.hir_id()) + && !item.span.from_expansion() + { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); } } @@ -157,8 +146,9 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { // functions with a body are already checked by `check_fn` if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind && fn_sig.header.abi == Abi::Rust + && fn_sig.decl.inputs.len() as u64 > self.max_fn_params_bools { - self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); + check_fn_decl(cx, fn_sig.decl, fn_sig.span, self.max_fn_params_bools); } } @@ -171,12 +161,13 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { span: Span, def_id: LocalDefId, ) { - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && get_parent_as_impl(cx.tcx, hir_id).map_or(true, |impl_item| impl_item.of_trait.is_none()) + && fn_decl.inputs.len() as u64 > self.max_fn_params_bools + && get_parent_as_impl(cx.tcx, cx.tcx.local_def_id_to_hir_id(def_id)) + .map_or(true, |impl_item| impl_item.of_trait.is_none()) { - self.check_fn_sig(cx, fn_decl, span); + check_fn_decl(cx, fn_decl, span, self.max_fn_params_bools); } } } From d409b5c700e1789a5b2d0b1dd1bd996bfd8bb320 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 02:03:09 -0400 Subject: [PATCH 035/806] `extra_unused_type_parameters`: * Inline `is_empty_exported_or_macro`. * Check if there are any generic parameters before walking the signature. --- .../src/extra_unused_type_parameters.rs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 7484f772e0823..843ab9d5d99a7 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -50,21 +50,6 @@ impl ExtraUnusedTypeParameters { avoid_breaking_exported_api, } } - - /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items - /// if the `avoid_breaking_exported_api` config option is set. - fn is_empty_exported_or_macro( - &self, - cx: &LateContext<'_>, - span: Span, - def_id: LocalDefId, - body_id: BodyId, - ) -> bool { - let body = cx.tcx.hir().body(body_id).value; - let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none()); - let is_exported = cx.effective_visibilities.is_exported(def_id); - in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api) - } } impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); @@ -266,10 +251,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { } } +fn is_empty_body(cx: &LateContext<'_>, body: BodyId) -> bool { + matches!(cx.tcx.hir().body(body).value.kind, ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none()) +} + impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Fn(_, generics, body_id) = item.kind - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !generics.params.is_empty() + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, generics); @@ -281,8 +273,12 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind + && !item.generics.params.is_empty() && trait_ref_of_method(cx, item.owner_id.def_id).is_none() - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) + && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, item.generics); walk_impl_item(&mut walker, item); From c32af26be02fb3c12d18c28e0c6f2b3c953fda3a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:22:03 -0400 Subject: [PATCH 036/806] Refactor `future_not_send` --- clippy_lints/src/future_not_send.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index cb1d0de1edff9..9a43bc69764e2 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -66,15 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner()); if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_super_predicates(def_id); - let mut is_future = false; - for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) { - if let Some(trait_pred) = p.as_trait_clause() { - if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { - is_future = true; - break; - } - } - } + let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { + p.as_trait_clause().is_some_and(|trait_pred| { + Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() + }) + }); if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); From 776a5238b7e217fd187074751c19c668960b8fbb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:41:06 -0400 Subject: [PATCH 037/806] `if_let_mutex`: Use `for_each_expr`. --- clippy_lints/src/if_let_mutex.rs | 65 ++++++++------------------------ 1 file changed, 15 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index a55836a972fb8..b38cc7b36a123 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{higher, SpanlessEq}; +use core::ops::ControlFlow; use rustc_errors::Diag; -use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -44,8 +45,6 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); impl<'tcx> LateLintPass<'tcx> for IfLetMutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let mut arm_visit = ArmVisitor { found_mutex: None, cx }; - let mut op_visit = OppVisitor { found_mutex: None, cx }; if let Some(higher::IfLet { let_expr, if_then, @@ -53,12 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { .. }) = higher::IfLet::hir(cx, expr) { - op_visit.visit_expr(let_expr); - if let Some(op_mutex) = op_visit.found_mutex { - arm_visit.visit_expr(if_then); - arm_visit.visit_expr(if_else); + let is_mutex_lock = |e: &'tcx Expr<'tcx>| { + if let Some(mutex) = is_mutex_lock_call(cx, e) { + ControlFlow::Break(mutex) + } else { + ControlFlow::Continue(()) + } + }; - if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) { + let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); + if let Some(op_mutex) = op_mutex { + let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); + if let Some(arm_mutex) = arm_mutex + && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) + { let diag = |diag: &mut Diag<'_, ()>| { diag.span_label( op_mutex.span, @@ -83,48 +90,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { } } -/// Checks if `Mutex::lock` is called in the `if let` expr. -pub struct OppVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -/// Checks if `Mutex::lock` is called in any of the branches. -pub struct ArmVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> { - self.found_mutex.and_then(|arm_mutex| { - SpanlessEq::new(self.cx) - .eq_expr(op_mutex, arm_mutex) - .then_some(arm_mutex) - }) - } -} - fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" From b26b820f3ffa1697a9dbd66fcf8f29f373556cdb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:47:29 -0400 Subject: [PATCH 038/806] Refactor `if_not_else`: * Check HIR tree first. * Merge lint calls. --- clippy_lints/src/if_not_else.rs | 63 ++++++++++++++------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 4dc1ff8377110..2f6daeeb90d9d 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -56,44 +56,33 @@ fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { } impl LateLintPass<'_> for IfNotElse { - fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - // While loops will be desugared to ExprKind::If. This will cause the lint to fire. - // To fix this, return early if this span comes from a macro or desugaring. - if item.span.from_expansion() { - return; - } - if let ExprKind::If(cond, _, Some(els)) = item.kind { - if let ExprKind::Block(..) = els.kind { - // Disable firing the lint in "else if" expressions. - if is_else_clause(cx.tcx, item) { - return; - } + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::If(cond, _, Some(els)) = e.kind + && let ExprKind::DropTemps(cond) = cond.kind + && let ExprKind::Block(..) = els.kind + { + let (msg, help) = match cond.kind { + ExprKind::Unary(UnOp::Not, _) => ( + "unnecessary boolean `not` operation", + "remove the `!` and swap the blocks of the `if`/`else`", + ), + // Don't lint on `… != 0`, as these are likely to be bit tests. + // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. + ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ), + _ => return, + }; - match cond.peel_drop_temps().kind { - ExprKind::Unary(UnOp::Not, _) => { - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary boolean `not` operation", - None, - "remove the `!` and swap the blocks of the `if`/`else`", - ); - }, - ExprKind::Binary(ref kind, _, lhs) if kind.node == BinOpKind::Ne && !is_zero_const(lhs, cx) => { - // Disable firing the lint on `… != 0`, as these are likely to be bit tests. - // For example, `if foo & 0x0F00 != 0 { … } else { … }` already is in the "proper" order. - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary `!=` operation", - None, - "change to `==` and swap the blocks of the `if`/`else`", - ); - }, - _ => (), - } + // `from_expansion` will also catch `while` loops which appear in the HIR as: + // ```rust + // loop { + // if cond { ... } else { break; } + // } + // ``` + if !e.span.from_expansion() && !is_else_clause(cx.tcx, e) { + span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help); } } } From 517e1ac225209a8c02e20cd8c148412b64850261 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:56:15 -0400 Subject: [PATCH 039/806] `if_then_some_else_none`: Check HIR tree before other checks. --- clippy_lints/src/if_then_some_else_none.rs | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0b20081521987..087bf9e9d056a 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -61,26 +61,6 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !self.msrv.meets(msrvs::BOOL_THEN) { - return; - } - - if in_external_macro(cx.sess(), expr.span) { - return; - } - - // We only care about the top-most `if` in the chain - if is_else_clause(cx.tcx, expr) { - return; - } - - // `bool::then()` and `bool::then_some()` are not const - if in_constant(cx, expr.hir_id) { - return; - } - - let ctxt = expr.span.ctxt(); - if let Some(higher::If { cond, then, @@ -89,9 +69,14 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && let ctxt = expr.span.ctxt() && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && !is_else_clause(cx.tcx, expr) + && !in_constant(cx, expr.hir_id) + && !in_external_macro(cx.sess(), expr.span) + && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; From 3869e934080b145f349dca2b296d750bf9a9f757 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 14:59:36 -0400 Subject: [PATCH 040/806] `ignored_unit_patterns`: Reorder checks. --- clippy_lints/src/ignored_unit_patterns.rs | 31 +++++++++++------------ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index a32201d80793c..54b8adbc8ac79 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -37,22 +37,21 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - if pat.span.from_expansion() { - return; - } - - match cx.tcx.parent_hir_node(pat.hir_id) { - Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { - // Ignore function parameters - return; - }, - Node::LetStmt(local) if local.ty.is_some() => { - // Ignore let bindings with explicit type - return; - }, - _ => {}, - } - if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() { + if matches!(pat.kind, PatKind::Wild) + && !pat.span.from_expansion() + && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() + { + match cx.tcx.parent_hir_node(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::LetStmt(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } span_lint_and_sugg( cx, IGNORED_UNIT_PATTERNS, From b58b491d162bbb4b8f417b8abfcce1c6083208b9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 15:06:14 -0400 Subject: [PATCH 041/806] `inconsistent_struct_constructor`: Check HIR tree first. --- clippy_lints/src/inconsistent_struct_constructor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 1075975f0a21e..5b0aadf35c62d 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !expr.span.from_expansion() - && let ExprKind::Struct(qpath, fields, base) = expr.kind + if let ExprKind::Struct(qpath, fields, base) = expr.kind + && fields.iter().all(|f| f.is_shorthand) + && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() && let Some(variant) = adt_def.variants().iter().next() - && fields.iter().all(|f| f.is_shorthand) { let mut def_order_map = FxHashMap::default(); for (idx, field) in variant.fields.iter().enumerate() { From 06f83b4e2603b9bb702383199e57480b88f2382a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 15:10:05 -0400 Subject: [PATCH 042/806] `index_refutable_slice`: Check HIR tree first. --- clippy_lints/src/index_refutable_slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 128461ce7bcf3..4ce2fa32fe56f 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -70,8 +70,8 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) - && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && self.msrv.meets(msrvs::SLICE_PATTERNS) && let found_slices = find_slice_values(cx, let_pat) From c73aceb0435ca35e47d0d8361ce49a74ff3ec70b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 11 Jun 2024 15:11:20 -0400 Subject: [PATCH 043/806] `indexing_slicing`: Check HIR tree first. --- clippy_lints/src/indexing_slicing.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index d54f2af65cdea..52073d2ee4cdc 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -102,13 +102,8 @@ impl IndexingSlicing { impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id)) - || is_from_proc_macro(cx, expr) - { - return; - } - if let ExprKind::Index(array, index, _) = &expr.kind + && (!self.suppress_restriction_lint_in_const || !cx.tcx.hir().is_inside_const_context(expr.hir_id)) && let expr_ty = cx.typeck_results().expr_ty(array) && let mut deref = deref_chain(cx, expr_ty) && deref.any(|l| { @@ -116,6 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { || l.peel_refs().is_array() || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr) }) + && !is_from_proc_macro(cx, expr) { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); From f493d71a38f9737e512b133598bcb78981619f58 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 12 Jun 2024 12:59:55 -0400 Subject: [PATCH 044/806] `infinite_iter`: Only lock the symbol table once. --- clippy_lints/src/infinite_iter.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 9ad02735878a4..fa7e7f6b76d16 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -226,13 +226,14 @@ const INFINITE_COLLECTORS: &[Symbol] = &[ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { + let method_str = method.ident.name.as_str(); for &(name, len) in &COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return is_infinite(cx, receiver); } } for &(name, len) in &POSSIBLY_COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return MaybeInfinite.and(is_infinite(cx, receiver)); } } From 36a14e3a12c41f8bdf7087d350a454d6fe05e29d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 13 Jun 2024 18:56:11 -0400 Subject: [PATCH 045/806] `misc`: * Delay macro checks. * Use `span_lint_hir`. --- clippy_lints/src/misc.rs | 43 +++++++++++++++------------------ tests/ui/toplevel_ref_arg.fixed | 2 ++ tests/ui/toplevel_ref_arg.rs | 2 ++ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 32c8731c53743..a9aafe7ed56cb 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -114,40 +114,35 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - span: Span, + _: Span, _: LocalDefId, ) { - if let FnKind::Closure = k { - // Does not apply to closures - return; - } - if in_external_macro(cx.tcx.sess, span) { - return; - } - for arg in iter_input_pats(decl, body) { - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { - return; - } - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind { - span_lint( - cx, - TOPLEVEL_REF_ARG, - arg.pat.span, - "`ref` directly on a function argument is ignored. \ - Consider using a reference type instead", - ); + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !in_external_macro(cx.tcx.sess, arg.span) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function argument is ignored. \ + Consider using a reference type instead", + ); + } } } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !in_external_macro(cx.tcx.sess, stmt.span) - && let StmtKind::Let(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index ff5cd7abbb69e..3eb47a5b5fd65 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -36,4 +36,6 @@ fn main() { // do not lint in external macro external!(let ref _y = 42;); + + fn f(#[allow(clippy::toplevel_ref_arg)] ref x: i32) {} } diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index ab79b8959605f..cd731387de976 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -36,4 +36,6 @@ fn main() { // do not lint in external macro external!(let ref _y = 42;); + + fn f(#[allow(clippy::toplevel_ref_arg)] ref x: i32) {} } From 58aa804ada9c13e8e5e89e6f25594d52ba835816 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 13 Jun 2024 19:00:18 -0400 Subject: [PATCH 046/806] `mismatching_type_param_order`: Delay macro check. --- clippy_lints/src/mismatching_type_param_order.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 934b9f490addf..748289454bea5 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -49,12 +49,12 @@ declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if !item.span.from_expansion() - && let ItemKind::Impl(imp) = &item.kind + if let ItemKind::Impl(imp) = &item.kind && let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind - && let Some(segment) = path.segments.iter().next() + && let [segment, ..] = path.segments && let Some(generic_args) = segment.args && !generic_args.args.is_empty() + && !item.span.from_expansion() { // get the name and span of the generic parameters in the Impl let mut impl_params = Vec::new(); From c22608823f5831f068f027deec09899d6971489a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 13 Jun 2024 19:20:21 -0400 Subject: [PATCH 047/806] `needless_borrowed_ref`: Check HIR tree first. --- clippy_lints/src/needless_borrowed_ref.rs | 142 ++++++++++------------ 1 file changed, 67 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index fb02f24c9dc67..098098718af39 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -37,83 +37,75 @@ declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { - if ref_pat.span.from_expansion() { - // OK, simple enough, lints doesn't check in macro. - return; - } - - // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { - let Node::Pat(pat) = node else { break }; - - if matches!(pat.kind, PatKind::Or(_)) { - return; + if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind + && !ref_pat.span.from_expansion() + && cx + .tcx + .hir() + .parent_iter(ref_pat.hir_id) + .map_while(|(_, parent)| if let Node::Pat(pat) = parent { Some(pat) } else { None }) + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + .all(|pat| !matches!(pat.kind, PatKind::Or(_))) + { + match pat.kind { + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + PatKind::Binding(BindingMode::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + ref_pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = ref_pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, } } - - // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { - return; - }; - - match pat.kind { - // Check sub_pat got a `ref` keyword (excluding `ref mut`). - PatKind::Binding(BindingMode::REF, _, ident, None) => { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - ref_pat.span, - "this pattern takes a reference on something that is being dereferenced", - |diag| { - // `&ref ident` - // ^^^^^ - let span = ref_pat.span.until(ident.span); - diag.span_suggestion_verbose( - span, - "try removing the `&ref` part", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` - PatKind::Slice( - before, - None - | Some(Pat { - kind: PatKind::Wild, .. - }), - after, - ) => { - check_subpatterns( - cx, - "dereferencing a slice pattern where every element takes a reference", - ref_pat, - pat, - itertools::chain(before, after), - ); - }, - PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { - check_subpatterns( - cx, - "dereferencing a tuple pattern where every element takes a reference", - ref_pat, - pat, - subpatterns, - ); - }, - PatKind::Struct(_, fields, _) => { - check_subpatterns( - cx, - "dereferencing a struct pattern where every field's pattern takes a reference", - ref_pat, - pat, - fields.iter().map(|field| field.pat), - ); - }, - _ => {}, - } } } From 139dad88497f88f1041d6240aee406ce3afd45c8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 13 Jun 2024 19:12:10 -0400 Subject: [PATCH 048/806] `mut_mut`: Delay macro check --- clippy_lints/src/mut_mut.rs | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index bc7b2c6b7c1b8..60372121a7a4c 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -35,33 +35,18 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { - if in_external_macro(cx.sess(), ty.span) { - return; - } - - if let hir::TyKind::Ref( - _, - hir::MutTy { - ty: pty, - mutbl: hir::Mutability::Mut, - }, - ) = ty.kind + if let hir::TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == hir::Mutability::Mut + && let hir::TyKind::Ref(_, mty) = mty.ty.kind + && mty.mutbl == hir::Mutability::Mut + && !in_external_macro(cx.sess(), ty.span) { - if let hir::TyKind::Ref( - _, - hir::MutTy { - mutbl: hir::Mutability::Mut, - .. - }, - ) = pty.kind - { - span_lint( - cx, - MUT_MUT, - ty.span, - "generally you want to avoid `&mut &mut _` if possible", - ); - } + span_lint( + cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); } } } From 7c17dd331bb5708152ef2c2dd91c67287f9d821d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 15 Jun 2024 02:13:32 -0400 Subject: [PATCH 049/806] `needless_for_each`: Check HIR tree first. --- clippy_lints/src/needless_for_each.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 143acc2b1cb76..6390e51f916b2 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -3,7 +3,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; @@ -55,16 +55,8 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { - return; - }; - - if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind - // Check the method name is `for_each`. - && method_name.ident.name == Symbol::intern("for_each") - // Check `for_each` is an associated function of `Iterator`. - && is_trait_method(cx, expr, sym::Iterator) - // Checks the receiver of `for_each` is also a method call. + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. @@ -72,6 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) + && method_name.ident.name.as_str() == "for_each" + && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. From ff28de20b4c9522cce639a8e5dbff433501ae265 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 15 Jun 2024 11:16:20 -0400 Subject: [PATCH 050/806] `neg_cmp_op_on_partial_ord`: Delay macro check --- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index f7621822b66c4..fa90ee606121b 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -45,10 +45,10 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Unary(UnOp::Not, inner) = expr.kind + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind && let ExprKind::Binary(ref op, left, _) = inner.kind && let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node + && !in_external_macro(cx.sess(), expr.span) { let ty = cx.typeck_results().expr_ty(left); From 15eac5abd684a38df7643183ef2874432d960d9c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 15 Jun 2024 17:32:04 -0400 Subject: [PATCH 051/806] Simplify `option_env_unwrap`. --- clippy_lints/src/option_env_unwrap.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index 4bfb26209d21d..d16f5f8e112c3 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -3,7 +3,7 @@ use clippy_utils::is_direct_expn_of; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -35,30 +35,18 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - fn lint(cx: &EarlyContext<'_>, span: Span) { + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind + && matches!(seg.ident.name, sym::expect | sym::unwrap) + && is_direct_expn_of(receiver.span, "option_env").is_some() + { span_lint_and_help( cx, OPTION_ENV_UNWRAP, - span, + expr.span, "this will panic at run-time if the environment variable doesn't exist at compile-time", None, "consider using the `env!` macro instead", ); } - - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind - && matches!(seg.ident.name, sym::expect | sym::unwrap) - { - if let ExprKind::Call(caller, _) = &receiver.kind && - // If it exists, it will be ::core::option::Option::Some("").unwrap() (A method call in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } - } } } From e12ff630fde5fb331b32cb2831fd164497df8f27 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 16 Jun 2024 01:41:56 -0400 Subject: [PATCH 052/806] `permissions_set_readonly_false`: Check HIR tree first. --- clippy_lints/src/permissions_set_readonly_false.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index 704acdc103e84..dc142b6e15771 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -31,10 +31,10 @@ declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALS impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) - && path.ident.name == sym!(set_readonly) && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node + && path.ident.name.as_str() == "set_readonly" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( cx, From 0c72a99cac2b8f53a77fe1acc1efef8559f19940 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 16 Jun 2024 15:42:28 -0400 Subject: [PATCH 053/806] `raw_strings` Check HIR tree first. --- clippy_lints/src/raw_strings.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 3a00424545922..e66e2ee47cd08 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -2,7 +2,7 @@ use std::iter::once; use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -63,20 +63,17 @@ pub struct RawStrings { impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Lit(lit) = expr.kind - && let LitKind::StrRaw(max) | LitKind::ByteStrRaw(max) | LitKind::CStrRaw(max) = lit.kind + if let ExprKind::Lit(lit) = expr.kind + && let (prefix, max) = match lit.kind { + LitKind::StrRaw(max) => ("r", max), + LitKind::ByteStrRaw(max) => ("br", max), + LitKind::CStrRaw(max) => ("cr", max), + _ => return, + } + && !in_external_macro(cx.sess(), expr.span) + && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) { let str = lit.symbol.as_str(); - let prefix = match lit.kind { - LitKind::StrRaw(..) => "r", - LitKind::ByteStrRaw(..) => "br", - LitKind::CStrRaw(..) => "cr", - _ => unreachable!(), - }; - if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) { - return; - } let descr = lit.kind.descr(); if !str.contains(['\\', '"']) { @@ -86,7 +83,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, 0, max); + let (start, end) = hash_spans(expr.span, prefix.len(), 0, max); // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); @@ -148,7 +145,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary hashes around raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, req, max); + let (start, end) = hash_spans(expr.span, prefix.len(), req, max); let message = match max - req { _ if req == 0 => format!("remove all the hashes around the {descr} literal"), @@ -174,11 +171,11 @@ impl EarlyLintPass for RawStrings { /// r###".."### /// ^^ ^^ /// ``` -fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) { +fn hash_spans(literal_span: Span, prefix_len: usize, req: u8, max: u8) -> (Span, Span) { let literal_span = literal_span.data(); // BytePos: we checked prefix appears literally in the source text - let hash_start = literal_span.lo + BytePos::from_usize(prefix.len()); + let hash_start = literal_span.lo + BytePos::from_usize(prefix_len); let hash_end = literal_span.hi; // BytePos: req/max are counts of the ASCII character # From 6519c143a7ae1494b38920cfb08a55793fbfe07d Mon Sep 17 00:00:00 2001 From: B I Mohammed Abbas Date: Mon, 8 Jul 2024 10:56:13 +0530 Subject: [PATCH 054/806] Reset sigpipe not supported for vxworks --- library/std/src/sys/pal/unix/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 16fc2011d7085..d016e5e32d404 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -164,6 +164,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", // Unikraft's `signal` implementation is currently broken: // https://github.com/unikraft/lib-musl/issues/57 target_vendor = "unikraft", From 32efd239554dd80d7983e160024e7d4cd6db3439 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Mon, 8 Jul 2024 11:40:04 -0700 Subject: [PATCH 055/806] Add target page for riscv64gc-unknown-linux-gnu --- src/doc/rustc/src/platform-support.md | 2 +- .../riscv64gc-unknown-linux-gnu.md | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index f5cd4bd217a32..0ecaaf5a9aae6 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -97,7 +97,7 @@ target | notes `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) `powerpc64le-unknown-linux-gnu` | PPC64LE Linux (kernel 3.10, glibc 2.17) -`riscv64gc-unknown-linux-gnu` | RISC-V Linux (kernel 4.20, glibc 2.29) +[`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17) `x86_64-unknown-freebsd` | 64-bit FreeBSD `x86_64-unknown-illumos` | illumos diff --git a/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md new file mode 100644 index 0000000000000..21d547f5aaba7 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md @@ -0,0 +1,127 @@ +# `riscv64gc-unknown-linux-gnu` + +**Tier: 2 (with Host Tools)** + +RISC-V targets using the *RV64I* base instruction set with the *G* collection of extensions, as well as the *C* extension. + + +## Target maintainers + +- TODO + + +## Requirements + +This target requires: + +* Linux Kernel version 4.20 or later +* glibc 2.17 or later + + +## Building the target + +These targets are distributed through `rustup`, and otherwise require no +special configuration. + +If you need to build your own Rust for some reason though, the targets can be +enabled in `config.toml`. For example: + +```toml +[build] +target = ["riscv64gc-unknown-linux-gnu"] +``` + + +## Building Rust programs + + +On a RISC-V host, the `riscv64gc-unknown-linux-gnu` target should be automatically +installed and used by default. + +On a non-RISC-V host, add the target: + +```bash +rustup target add riscv64gc-unknown-linux-gnu +``` + +Then cross compile crates with: + +```bash +cargo build --target riscv64gc-unknown-linux-gnu +``` + + +## Testing + +There are no special requirements for testing and running the targets. +For testing cross builds on the host, please refer to the "Cross-compilation +toolchains and C code" +section below. + + +## Cross-compilation toolchains and C code + +A RISC-V toolchain can be obtained for Windows/Mac/Linux from the +[`riscv-gnu-toolchain`](https://github.com/riscv-collab/riscv-gnu-toolchain) +repostory. Binaries are available via +[embecosm](https://www.embecosm.com/resources/tool-chain-downloads/#riscv-linux), +and may also be available from your OS's package manager. + +On Ubuntu, a RISC-V toolchain can be installed with: + +```bash +apt install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross +``` + +Depending on your system, you may need to configure the target to use the GNU +GCC linker. To use it, add the following to your `.cargo/config.toml`: + +```toml +[target.riscv64gc-unknown-linux-gnu] +linker = "riscv64-linux-gnu-gcc" +``` + +If your `riscv64-linux-gnu-*` toolchain is not in your `PATH` you may need to +configure additional settings: + +```toml +[target.riscv64gc-unknown-linux-gnu] +# Adjust the paths to point at your toolchain +cc = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-gcc" +cxx = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-g++" +ar = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-ar" +ranlib = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-ranlib" +linker = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-gcc" +``` + +To test cross compiled binaries on a non-RISCV-V host, you can use +[`qemu`](https://www.qemu.org/docs/master/system/target-riscv.html). +On Ubuntu, a RISC-V emulator can be obtained with: + +```bash +apt install qemu-system-riscv64 +``` + +Then, in `.cargo/config.toml` set the `runner`: + +```toml +[target.riscv64gc-unknown-linux-gnu] +runner = "qemu-riscv64-static -L /usr/riscv64-linux-gnu -cpu rv64" +``` + +On Mac and Linux, it's also possible to use +[`lima`](https://github.com/lima-vm/lima) to emulate RISC-V in a similar way to +how WSL2 works on Windows: + +```bash +limactl start template://riscv +limactl shell riscv +``` + +Using [Docker (with BuildKit)](https://docs.docker.com/build/buildkit/) the +[`riscv64/ubuntu`](https://hub.docker.com/r/riscv64/ubuntu) image can be used +to buiild or run `riscv64gc-unknown-linux-gnu` binaries. + +```bash +docker run --platform linux/riscv64 -ti --rm --mount "type=bind,src=$(pwd),dst=/checkout" riscv64/ubuntu bash +``` From 296dcf09fb7e744c8f7b118e2e328dbc1fd6a7a5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 19 Apr 2024 09:26:52 +0100 Subject: [PATCH 056/806] unnecessary_struct_initialization: extend to assignments moving all fields This lint makes Clippy warn about situations where an owned struct is essentially recreated by moving all its fields into a new instance of the struct. Until now this lint only triggered for structs recreated from a base struct. NB: The new functionality too will cause false positives for the situation where a non-copy struct consisting of all copy members is touched again in subsequent code. --- .../src/unnecessary_struct_initialization.rs | 187 ++++++++++++++---- .../unnecessary_struct_initialization.fixed | 70 +++++++ tests/ui/unnecessary_struct_initialization.rs | 70 +++++++ .../unnecessary_struct_initialization.stderr | 44 ++++- 4 files changed, 328 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 2da7533434484..afdd3505cdd16 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -2,14 +2,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use clippy_utils::{get_parent_expr, path_to_local}; -use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind, UnOp}; +use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for initialization of a `struct` by copying a base without setting - /// any field. + /// Checks for initialization of an identical `struct` from another instance + /// of the type, either by copying a base without setting any field or by + /// moving all fields individually. /// /// ### Why is this bad? /// Readability suffers from unnecessary struct building. @@ -29,9 +30,14 @@ declare_clippy_lint! { /// let b = a; /// ``` /// + /// The struct literal ``S { ..a }`` in the assignment to ``b`` could be replaced + /// with just ``a``. + /// /// ### Known Problems /// Has false positives when the base is a place expression that cannot be /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547). + /// + /// Empty structs are ignored by the lint. #[clippy::version = "1.70.0"] pub UNNECESSARY_STRUCT_INITIALIZATION, nursery, @@ -41,42 +47,111 @@ declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]); impl LateLintPass<'_> for UnnecessaryStruct { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Struct(_, &[], Some(base)) = expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) - && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) - && parent_ty.is_any_ptr() - { - if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() { - // When the type implements `Copy`, a reference to the new struct works on the - // copy. Using the original would borrow it. - return; - } - - if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) { - // The original can be used in a mutable reference context only if it is mutable. - return; - } - } + let ExprKind::Struct(_, fields, base) = expr.kind else { + return; + }; - // TODO: do not propose to replace *XX if XX is not Copy - if let ExprKind::Unary(UnOp::Deref, target) = base.kind - && matches!(target.kind, ExprKind::Path(..)) - && !is_copy(cx, cx.typeck_results().expr_ty(expr)) - { - // `*base` cannot be used instead of the struct in the general case if it is not Copy. - return; - } + if expr.span.from_expansion() { + // Prevent lint from hitting inside macro code + return; + } + + let field_path = same_path_in_all_fields(cx, expr, fields); + + let sugg = match (field_path, base) { + (Some(&path), None) => { + // all fields match, no base given + path.span + }, + (Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => { + // all fields match, has base: ensure that the path of the base matches + base.span + }, + (None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => { + // just the base, no explicit fields + base.span + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_STRUCT_INITIALIZATION, + expr.span, + "unnecessary struct building", + "replace with", + snippet(cx, sugg, "..").into_owned(), + rustc_errors::Applicability::MachineApplicable, + ); + } +} + +fn base_is_suitable(cx: &LateContext<'_>, expr: &Expr<'_>, base: &Expr<'_>) -> bool { + if !check_references(cx, expr, base) { + return false; + } + + // TODO: do not propose to replace *XX if XX is not Copy + if let ExprKind::Unary(UnOp::Deref, target) = base.kind + && matches!(target.kind, ExprKind::Path(..)) + && !is_copy(cx, cx.typeck_results().expr_ty(expr)) + { + // `*base` cannot be used instead of the struct in the general case if it is not Copy. + return false; + } + true +} + +/// Check whether all fields of a struct assignment match. +/// Returns a [Path] item that one can obtain a span from for the lint suggestion. +/// +/// Conditions that must be satisfied to trigger this variant of the lint: +/// +/// - source struct of the assignment must be of same type as the destination +/// - names of destination struct fields must match the field names of the source +/// +/// We don’t check here if all struct fields are assigned as the remainder may +/// be filled in from a base struct. +fn same_path_in_all_fields<'tcx>( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fields: &[ExprField<'tcx>], +) -> Option<&'tcx Path<'tcx>> { + let ty = cx.typeck_results().expr_ty(expr); + + let mut found = None; - span_lint_and_sugg( - cx, - UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, - "unnecessary struct building", - "replace with", - snippet(cx, base.span, "..").into_owned(), - rustc_errors::Applicability::MachineApplicable, - ); + for f in fields { + // fields are assigned from expression + if let ExprKind::Field(src_expr, ident) = f.expr.kind + // expression type matches + && ty == cx.typeck_results().expr_ty(src_expr) + // field name matches + && f.ident == ident + // assigned from a path expression + && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind + { + let Some((_, p)) = found else { + // this is the first field assignment in the list + found = Some((src_expr, src_path)); + continue; + }; + + if p.res == src_path.res { + // subsequent field assignment with same origin struct as before + continue; + } } + // source of field assignment doesn’t qualify + return None; + } + + if let Some((src_expr, src_path)) = found + && check_references(cx, expr, src_expr) + { + Some(src_path) + } else { + None } } @@ -89,3 +164,43 @@ fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, expr_a) + && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) + && parent_ty.is_any_ptr() + { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + // When the type implements `Copy`, a reference to the new struct works on the + // copy. Using the original would borrow it. + return false; + } + + if parent_ty.is_mutable_ptr() && !is_mutable(cx, expr_b) { + // The original can be used in a mutable reference context only if it is mutable. + return false; + } + } + + true +} + +/// When some fields are assigned from a base struct and others individually +/// the lint applies only if the source of the field is the same as the base. +/// This is enforced here by comparing the path of the base expression; +/// needless to say the lint only applies if it (or whatever expression it is +/// a reference of) actually has a path. +fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { + let base_path = match base.kind { + ExprKind::Unary(UnOp::Deref, base_expr) => { + if let ExprKind::Path(QPath::Resolved(_, base_path)) = base_expr.kind { + base_path + } else { + return false; + } + }, + ExprKind::Path(QPath::Resolved(_, base_path)) => base_path, + _ => return false, + }; + path.res == base_path.res +} diff --git a/tests/ui/unnecessary_struct_initialization.fixed b/tests/ui/unnecessary_struct_initialization.fixed index f3cf65da2d6ad..b0e8f45463539 100644 --- a/tests/ui/unnecessary_struct_initialization.fixed +++ b/tests/ui/unnecessary_struct_initialization.fixed @@ -26,6 +26,11 @@ struct V { f: u32, } +struct W { + f1: u32, + f2: u32, +} + impl Clone for V { fn clone(&self) -> Self { // Lint: `Self` implements `Copy` @@ -68,4 +73,69 @@ fn main() { // Should lint: the result of an expression is mutable and temporary let p = &mut *Box::new(T { f: 5 }); + + // Should lint: all fields of `q` would be consumed anyway + let q = W { f1: 42, f2: 1337 }; + let r = q; + + // Should not lint: not all fields of `t` from same source + let s = W { f1: 1337, f2: 42 }; + let t = W { f1: s.f1, f2: r.f2 }; + + // Should not lint: different fields of `s` assigned + let u = W { f1: s.f2, f2: s.f1 }; + + // Should lint: all fields of `v` would be consumed anyway + let v = W { f1: 42, f2: 1337 }; + let w = v; + + // Should not lint: source differs between fields and base + let x = W { f1: 42, f2: 1337 }; + let y = W { f1: w.f1, ..x }; + + // Should lint: range desugars to struct + let r1 = 0..5; + let r2 = r1; + + references(); + shorthand(); +} + +fn references() { + // Should not lint as `a` is not mutable + let a = W { f1: 42, f2: 1337 }; + let b = &mut W { f1: a.f1, f2: a.f2 }; + + // Should lint as `d` is a shared reference + let c = W { f1: 42, f2: 1337 }; + let d = &c; + + // Should not lint as `e` is not mutable + let e = W { f1: 42, f2: 1337 }; + let f = &mut W { f1: e.f1, ..e }; + + // Should lint as `h` is a shared reference + let g = W { f1: 42, f2: 1337 }; + let h = &g; + + // Should not lint as `j` is copy + let i = V { f: 0x1701d }; + let j = &V { ..i }; + + // Should not lint as `k` is copy + let k = V { f: 0x1701d }; + let l = &V { f: k.f }; +} + +fn shorthand() { + struct S1 { + a: i32, + b: i32, + } + + let a = 42; + let s = S1 { a: 3, b: 4 }; + + // Should not lint: `a` is not from `s` + let s = S1 { a, b: s.b }; } diff --git a/tests/ui/unnecessary_struct_initialization.rs b/tests/ui/unnecessary_struct_initialization.rs index bd5302f9d85db..b0db71af4d4c2 100644 --- a/tests/ui/unnecessary_struct_initialization.rs +++ b/tests/ui/unnecessary_struct_initialization.rs @@ -26,6 +26,11 @@ struct V { f: u32, } +struct W { + f1: u32, + f2: u32, +} + impl Clone for V { fn clone(&self) -> Self { // Lint: `Self` implements `Copy` @@ -72,4 +77,69 @@ fn main() { let p = &mut T { ..*Box::new(T { f: 5 }) }; + + // Should lint: all fields of `q` would be consumed anyway + let q = W { f1: 42, f2: 1337 }; + let r = W { f1: q.f1, f2: q.f2 }; + + // Should not lint: not all fields of `t` from same source + let s = W { f1: 1337, f2: 42 }; + let t = W { f1: s.f1, f2: r.f2 }; + + // Should not lint: different fields of `s` assigned + let u = W { f1: s.f2, f2: s.f1 }; + + // Should lint: all fields of `v` would be consumed anyway + let v = W { f1: 42, f2: 1337 }; + let w = W { f1: v.f1, ..v }; + + // Should not lint: source differs between fields and base + let x = W { f1: 42, f2: 1337 }; + let y = W { f1: w.f1, ..x }; + + // Should lint: range desugars to struct + let r1 = 0..5; + let r2 = r1.start..r1.end; + + references(); + shorthand(); +} + +fn references() { + // Should not lint as `a` is not mutable + let a = W { f1: 42, f2: 1337 }; + let b = &mut W { f1: a.f1, f2: a.f2 }; + + // Should lint as `d` is a shared reference + let c = W { f1: 42, f2: 1337 }; + let d = &W { f1: c.f1, f2: c.f2 }; + + // Should not lint as `e` is not mutable + let e = W { f1: 42, f2: 1337 }; + let f = &mut W { f1: e.f1, ..e }; + + // Should lint as `h` is a shared reference + let g = W { f1: 42, f2: 1337 }; + let h = &W { f1: g.f1, ..g }; + + // Should not lint as `j` is copy + let i = V { f: 0x1701d }; + let j = &V { ..i }; + + // Should not lint as `k` is copy + let k = V { f: 0x1701d }; + let l = &V { f: k.f }; +} + +fn shorthand() { + struct S1 { + a: i32, + b: i32, + } + + let a = 42; + let s = S1 { a: 3, b: 4 }; + + // Should not lint: `a` is not from `s` + let s = S1 { a, b: s.b }; } diff --git a/tests/ui/unnecessary_struct_initialization.stderr b/tests/ui/unnecessary_struct_initialization.stderr index 8bc308c7567b5..56982cc0a39f9 100644 --- a/tests/ui/unnecessary_struct_initialization.stderr +++ b/tests/ui/unnecessary_struct_initialization.stderr @@ -1,5 +1,5 @@ error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:32:9 + --> tests/ui/unnecessary_struct_initialization.rs:37:9 | LL | Self { ..*self } | ^^^^^^^^^^^^^^^^ help: replace with: `*self` @@ -8,25 +8,25 @@ LL | Self { ..*self } = help: to override `-D warnings` add `#[allow(clippy::unnecessary_struct_initialization)]` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:39:17 + --> tests/ui/unnecessary_struct_initialization.rs:44:17 | LL | let mut b = S { ..a }; | ^^^^^^^^^ help: replace with: `a` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:42:18 + --> tests/ui/unnecessary_struct_initialization.rs:47:18 | LL | let c = &mut S { ..b }; | ^^^^^^^^^ help: replace with: `b` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:50:14 + --> tests/ui/unnecessary_struct_initialization.rs:55:14 | LL | let g = &S { ..f }; | ^^^^^^^^^ help: replace with: `f` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:53:18 + --> tests/ui/unnecessary_struct_initialization.rs:58:18 | LL | let h = &mut S { | __________________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: replace with: `*Box::new(S { f: String::from("foo") })` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:72:18 + --> tests/ui/unnecessary_struct_initialization.rs:77:18 | LL | let p = &mut T { | __________________^ @@ -43,5 +43,35 @@ LL | | ..*Box::new(T { f: 5 }) LL | | }; | |_____^ help: replace with: `*Box::new(T { f: 5 })` -error: aborting due to 6 previous errors +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:83:13 + | +LL | let r = W { f1: q.f1, f2: q.f2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `q` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:94:13 + | +LL | let w = W { f1: v.f1, ..v }; + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `v` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:102:14 + | +LL | let r2 = r1.start..r1.end; + | ^^^^^^^^^^^^^^^^ help: replace with: `r1` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:115:14 + | +LL | let d = &W { f1: c.f1, f2: c.f2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `c` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:123:14 + | +LL | let h = &W { f1: g.f1, ..g }; + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `g` + +error: aborting due to 11 previous errors From 22364f86aeba440066db931132ecee8292b709fd Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Tue, 9 Jul 2024 14:24:05 +0200 Subject: [PATCH 057/806] This pattern using lazy protected Reserved IM prevents spurious writes --- .../tree_borrows/reservedim_spurious_write.rs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs new file mode 100644 index 0000000000000..1f52537f9a295 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -0,0 +1,109 @@ +// Illustrating a problematic interaction between Reserved, interior mutability, +// and protectors, that makes spurious writes fail in the previous model of Tree Borrows. +// As for all similar tests, we disable preemption so that the error message is deterministic. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0 + +use std::cell::Cell; +use std::sync::{Arc, Barrier}; +use std::thread; + +// Here is the problematic interleaving: +// - thread 1: retag and activate `x` (protected) +// - thread 2: create but do not retag (lazy) `y` as Reserved with interior mutability +// - thread 1: spurious write through `x` would go here +// - thread 2: function exit (noop due to lazyness) +// - thread 1: function exit (no permanent effect on `y` because it is now Reserved IM unprotected) +// - thread 2: write through `y` +// In the source code nothing happens to `y` + +// `Send`able raw pointer wrapper. +#[derive(Copy, Clone)] +struct SendPtr(*mut u8); +unsafe impl Send for SendPtr {} + +type IdxBarrier = (usize, Arc); + +// Barriers to enforce the interleaving. +// This macro expects `synchronized!(thread, msg)` where `thread` is a `IdxBarrier`, +// and `msg` is the message to be displayed when the thread reaches this point in the execution. +macro_rules! synchronized { + ($thread:expr, $msg:expr) => {{ + let (thread_id, barrier) = &$thread; + eprintln!("Thread {} executing: {}", thread_id, $msg); + barrier.wait(); + }}; +} + +fn main() { + eprintln!("Without spurious write"); + example(false); + + eprintln!("\nIf this text is visible then the model forbids spurious writes.\n"); + + eprintln!("With spurious write"); + example(true); + + eprintln!("\nIf this text is visible then the model fails to detect a noalias violation.\n"); +} + +fn example(spurious: bool) { + // For this example it is important that we have at least two bytes + // because lazyness is involved. + let mut data = [0u8; 2]; + let ptr = SendPtr(std::ptr::addr_of_mut!(data[0])); + let barrier = Arc::new(Barrier::new(2)); + let bx = Arc::clone(&barrier); + let by = Arc::clone(&barrier); + + // Retag and activate `x`, wait until the other thread creates a lazy permission. + // Then do a spurious write. Finally exit the function after the other thread. + let thread_1 = thread::spawn(move || { + let b = (1, bx); + synchronized!(b, "start"); + let ptr = ptr; + synchronized!(b, "retag x (&mut, protect)"); + fn inner(x: &mut u8, b: IdxBarrier, spurious: bool) { + *x = 42; // activate immediately + synchronized!(b, "[lazy] retag y (&mut, protect, IM)"); + // A spurious write should be valid here because `x` is + // `Active` and protected. + if spurious { + synchronized!(b, "spurious write x (executed)"); + *x = 64; + } else { + synchronized!(b, "spurious write x (skipped)"); + } + synchronized!(b, "ret y"); + synchronized!(b, "ret x"); + } + inner(unsafe { &mut *ptr.0 }, b.clone(), spurious); + synchronized!(b, "write y"); + synchronized!(b, "end"); + }); + + // Create a lazy Reserved with interior mutability. + // Wait for the other thread's spurious write then observe the side effects + // of that write. + let thread_2 = thread::spawn(move || { + let b = (2, by); + synchronized!(b, "start"); + let ptr = ptr; + synchronized!(b, "retag x (&mut, protect)"); + synchronized!(b, "[lazy] retag y (&mut, protect, IM)"); + fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { + synchronized!(b, "spurious write x"); + synchronized!(b, "ret y"); + y as *mut Cell as *mut u8 + } + // Currently `ptr` points to `data[0]`. We retag it for `data[1]` + // then use it for `data[0]` where its initialization has been deferred. + let y = inner(unsafe { &mut *(ptr.0 as *mut Cell).wrapping_add(1) }, b.clone()); + synchronized!(b, "ret x"); + synchronized!(b, "write y"); + unsafe { *y.wrapping_sub(1) = 13 } + synchronized!(b, "end"); + }); + + thread_1.join().unwrap(); + thread_2.join().unwrap(); +} From 2de6e7f3a680a8000f83f2b62252d18a1bb06966 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Tue, 9 Jul 2024 14:36:46 +0200 Subject: [PATCH 058/806] Implement fix for reservedim_spurious_write: ignore IM on protected --- .../src/borrow_tracker/tree_borrows/mod.rs | 10 ++++- .../src/borrow_tracker/tree_borrows/perms.rs | 5 +++ .../reserved/cell-protected-write.stderr | 6 +-- .../tree_borrows/reservedim_spurious_write.rs | 2 +- .../reservedim_spurious_write.stderr | 41 +++++++++++++++++++ .../tests/pass/tree_borrows/reserved.stderr | 2 +- 6 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 86074384084d5..4d9595b352f28 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -141,8 +141,14 @@ impl<'tcx> NewPermission { ) -> Option { let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env()); let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env()); + let is_protected = kind == RetagKind::FnEntry; + // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, + // interior mutability and protectors interact poorly. + // To eliminate the case of Protected Reserved IM we override interior mutability + // in the case of a protected reference. let initial_state = match mutability { - Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze), + Mutability::Mut if ty_is_unpin => + Permission::new_reserved(ty_is_freeze || is_protected), Mutability::Not if ty_is_freeze => Permission::new_frozen(), // Raw pointers never enter this function so they are not handled. // However raw pointers are not the only pointers that take the parent @@ -151,7 +157,7 @@ impl<'tcx> NewPermission { _ => return None, }; - let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector); + let protector = is_protected.then_some(ProtectorKind::StrongProtector); Some(Self { zero_size: false, initial_state, protector }) } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 7aa9c3e862bcc..9c19ae76a2d01 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -22,6 +22,11 @@ enum PermissionPriv { /// - foreign-read then child-write is UB due to `conflicted`, /// - child-write then foreign-read is UB since child-write will activate and then /// foreign-read disables a protected `Active`, which is UB. + /// + /// Note: since the discovery of `tests/fail/tree_borrows/reservedim_spurious_write.rs`, + /// `ty_is_freeze` does not strictly mean that the type has no interior mutability, + /// it could be an interior mutable type that lost its interior mutability privileges + /// when retagged with a protector. Reserved { ty_is_freeze: bool, conflicted: bool }, /// represents: a unique pointer; /// allows: child reads, child writes; diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr index 7d000ba55e60b..ce9a5b7f15865 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr @@ -5,7 +5,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. | RsM | └─┬── | RsM | ├─┬── | RsM | │ └─┬── -| RsM | │ └──── Strongly protected +| Rs | │ └──── Strongly protected | RsM | └──── ────────────────────────────────────────────────── error: Undefined Behavior: write access through (y, callee:y, caller:y) at ALLOC[0x0] is forbidden @@ -16,14 +16,14 @@ LL | *y = 1; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag (y, callee:y, caller:y) is foreign to the protected tag (callee:x) (i.e., it is not a child) - = help: this foreign write access would cause the protected tag (callee:x) (currently Reserved (interior mutable)) to become Disabled + = help: this foreign write access would cause the protected tag (callee:x) (currently Reserved) to become Disabled = help: protected tags must never be Disabled help: the accessed tag was created here --> $DIR/cell-protected-write.rs:LL:CC | LL | let y = (&mut *n).get(); | ^^^^^^^^^ -help: the protected tag was created here, in the initial state Reserved (interior mutable) +help: the protected tag was created here, in the initial state Reserved --> $DIR/cell-protected-write.rs:LL:CC | LL | unsafe fn write_second(x: &mut UnsafeCell, y: *mut u8) { diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs index 1f52537f9a295..5e7cb5e34cc2b 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -100,7 +100,7 @@ fn example(spurious: bool) { let y = inner(unsafe { &mut *(ptr.0 as *mut Cell).wrapping_add(1) }, b.clone()); synchronized!(b, "ret x"); synchronized!(b, "write y"); - unsafe { *y.wrapping_sub(1) = 13 } + unsafe { *y.wrapping_sub(1) = 13 } //~ERROR: /write access through .* is forbidden/ synchronized!(b, "end"); }); diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr new file mode 100644 index 0000000000000..9be6c157c0cbb --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr @@ -0,0 +1,41 @@ +Without spurious write +Thread 1 executing: start +Thread 2 executing: start +Thread 2 executing: retag x (&mut, protect) +Thread 1 executing: retag x (&mut, protect) +Thread 1 executing: [lazy] retag y (&mut, protect, IM) +Thread 2 executing: [lazy] retag y (&mut, protect, IM) +Thread 2 executing: spurious write x +Thread 1 executing: spurious write x (skipped) +Thread 1 executing: ret y +Thread 2 executing: ret y +Thread 2 executing: ret x +Thread 1 executing: ret x +Thread 1 executing: write y +Thread 2 executing: write y +error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | unsafe { *y.wrapping_sub(1) = 13 } + | ^^^^^^^^^^^^^^^^^^^^^^^ write access through at ALLOC[0x0] is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { + | ^ +help: the accessed tag later transitioned to Disabled due to a protector release (acting as a foreign write access) on every location previously accessed by this tag + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | } + | ^ + = help: this transition corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at $DIR/reservedim_spurious_write.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr index 0d0d52c717fed..d149a4065f92b 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr @@ -6,7 +6,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. | RsM | └─┬── | RsM | ├─┬── | RsM | │ └─┬── -| RsCM| │ └──── +| RsC | │ └──── | RsM | └──── ────────────────────────────────────────────────── [interior mut] Foreign Read: Re* -> Re* From 22996ad073870c5ac7753ba1acbaba784adc534d Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Tue, 9 Jul 2024 19:42:12 +0200 Subject: [PATCH 059/806] Apply suggestions - split test into two revisions - clarify comments --- .../src/borrow_tracker/tree_borrows/mod.rs | 3 +- .../tree_borrows/reservedim_spurious_write.rs | 26 +++++------- .../reservedim_spurious_write.with.stderr | 40 +++++++++++++++++++ ... reservedim_spurious_write.without.stderr} | 1 - 4 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr rename src/tools/miri/tests/fail/tree_borrows/{reservedim_spurious_write.stderr => reservedim_spurious_write.without.stderr} (98%) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 4d9595b352f28..123d4b407fb4c 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -145,7 +145,8 @@ impl<'tcx> NewPermission { // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, // interior mutability and protectors interact poorly. // To eliminate the case of Protected Reserved IM we override interior mutability - // in the case of a protected reference. + // in the case of a protected reference: protected references are always considered + // "freeze". let initial_state = match mutability { Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze || is_protected), diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs index 5e7cb5e34cc2b..611f64dce5e50 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -2,6 +2,12 @@ // and protectors, that makes spurious writes fail in the previous model of Tree Borrows. // As for all similar tests, we disable preemption so that the error message is deterministic. //@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0 +// +// One revision without spurious read (default source code) and one with spurious read. +// Both are expected to be UB. Both revisions are expected to have the *same* error +// because we are aligning the behavior of `without` to that of `with` so that the +// spurious write is effectively a noop in the long term. +//@revisions: without with use std::cell::Cell; use std::sync::{Arc, Barrier}; @@ -9,7 +15,7 @@ use std::thread; // Here is the problematic interleaving: // - thread 1: retag and activate `x` (protected) -// - thread 2: create but do not retag (lazy) `y` as Reserved with interior mutability +// - thread 2: retag but do not initialize (lazy) `y` as Reserved with interior mutability // - thread 1: spurious write through `x` would go here // - thread 2: function exit (noop due to lazyness) // - thread 1: function exit (no permanent effect on `y` because it is now Reserved IM unprotected) @@ -35,18 +41,6 @@ macro_rules! synchronized { } fn main() { - eprintln!("Without spurious write"); - example(false); - - eprintln!("\nIf this text is visible then the model forbids spurious writes.\n"); - - eprintln!("With spurious write"); - example(true); - - eprintln!("\nIf this text is visible then the model fails to detect a noalias violation.\n"); -} - -fn example(spurious: bool) { // For this example it is important that we have at least two bytes // because lazyness is involved. let mut data = [0u8; 2]; @@ -62,12 +56,12 @@ fn example(spurious: bool) { synchronized!(b, "start"); let ptr = ptr; synchronized!(b, "retag x (&mut, protect)"); - fn inner(x: &mut u8, b: IdxBarrier, spurious: bool) { + fn inner(x: &mut u8, b: IdxBarrier) { *x = 42; // activate immediately synchronized!(b, "[lazy] retag y (&mut, protect, IM)"); // A spurious write should be valid here because `x` is // `Active` and protected. - if spurious { + if cfg!(with) { synchronized!(b, "spurious write x (executed)"); *x = 64; } else { @@ -76,7 +70,7 @@ fn example(spurious: bool) { synchronized!(b, "ret y"); synchronized!(b, "ret x"); } - inner(unsafe { &mut *ptr.0 }, b.clone(), spurious); + inner(unsafe { &mut *ptr.0 }, b.clone()); synchronized!(b, "write y"); synchronized!(b, "end"); }); diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr new file mode 100644 index 0000000000000..5ac9c912ba9ee --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr @@ -0,0 +1,40 @@ +Thread 1 executing: start +Thread 2 executing: start +Thread 2 executing: retag x (&mut, protect) +Thread 1 executing: retag x (&mut, protect) +Thread 1 executing: [lazy] retag y (&mut, protect, IM) +Thread 2 executing: [lazy] retag y (&mut, protect, IM) +Thread 2 executing: spurious write x +Thread 1 executing: spurious write x (executed) +Thread 1 executing: ret y +Thread 2 executing: ret y +Thread 2 executing: ret x +Thread 1 executing: ret x +Thread 1 executing: write y +Thread 2 executing: write y +error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | unsafe { *y.wrapping_sub(1) = 13 } + | ^^^^^^^^^^^^^^^^^^^^^^^ write access through at ALLOC[0x0] is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { + | ^ +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1] + --> $DIR/reservedim_spurious_write.rs:LL:CC + | +LL | *x = 64; + | ^^^^^^^ + = help: this transition corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at $DIR/reservedim_spurious_write.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr similarity index 98% rename from src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr rename to src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr index 9be6c157c0cbb..97c71fdedefb3 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr @@ -1,4 +1,3 @@ -Without spurious write Thread 1 executing: start Thread 2 executing: start Thread 2 executing: retag x (&mut, protect) From 601a61fe276a92bbde6e74d60fc493e064677d9d Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 9 Jul 2024 23:51:31 +0200 Subject: [PATCH 060/806] Lintcheck: Refactor `CrateSource` --- lintcheck/src/input.rs | 65 ++++++++++++++++++++---------------------- lintcheck/src/main.rs | 10 ++----- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 3d034391c280f..8e048615f43e1 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -37,28 +37,22 @@ struct TomlCrate { /// Represents an archive we download from crates.io, or a git repo, or a local repo/folder /// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct CrateWithSource { + pub name: String, + pub source: CrateSource, + pub options: Option>, +} + #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] pub enum CrateSource { - CratesIo { - name: String, - version: String, - options: Option>, - }, - Git { - name: String, - url: String, - commit: String, - options: Option>, - }, - Path { - name: String, - path: PathBuf, - options: Option>, - }, + CratesIo { version: String }, + Git { url: String, commit: String }, + Path { path: PathBuf }, } /// Read a `lintcheck_crates.toml` file -pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { +pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = @@ -71,23 +65,29 @@ pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let mut crate_sources = Vec::new(); for tk in tomlcrates { if let Some(ref path) = tk.path { - crate_sources.push(CrateSource::Path { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - path: PathBuf::from(path), + source: CrateSource::Path { + path: PathBuf::from(path), + }, options: tk.options.clone(), }); } else if let Some(ref version) = tk.version { - crate_sources.push(CrateSource::CratesIo { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - version: version.to_string(), + source: CrateSource::CratesIo { + version: version.to_string(), + }, options: tk.options.clone(), }); } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source - crate_sources.push(CrateSource::Git { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - url: tk.git_url.clone().unwrap(), - commit: tk.git_hash.clone().unwrap(), + source: CrateSource::Git { + url: tk.git_url.clone().unwrap(), + commit: tk.git_hash.clone().unwrap(), + }, options: tk.options.clone(), }); } else { @@ -117,7 +117,7 @@ pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { (crate_sources, crate_list.recursive) } -impl CrateSource { +impl CrateWithSource { /// Makes the sources available on the disk for clippy to check. /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or /// copies a local folder @@ -139,8 +139,10 @@ impl CrateSource { retries += 1; } } - match self { - CrateSource::CratesIo { name, version, options } => { + let name = &self.name; + let options = &self.options; + match &self.source { + CrateSource::CratesIo { version } => { let extract_dir = PathBuf::from(LINTCHECK_SOURCES); let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); @@ -173,12 +175,7 @@ impl CrateSource { options: options.clone(), } }, - CrateSource::Git { - name, - url, - commit, - options, - } => { + CrateSource::Git { url, commit } => { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo @@ -219,7 +216,7 @@ impl CrateSource { options: options.clone(), } }, - CrateSource::Path { name, path, options } => { + CrateSource::Path { path } => { fn is_cache_dir(entry: &DirEntry) -> bool { fs::read(entry.path().join("CACHEDIR.TAG")) .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index e37ffab13ac89..e85d70f11ed6d 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -38,7 +38,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::{env, fs}; use cargo_metadata::Message; -use input::{read_crates, CrateSource}; +use input::read_crates; use output::{ClippyCheckOutput, ClippyWarning, RustcIce}; use rayon::prelude::*; @@ -292,13 +292,7 @@ fn lintcheck(config: LintcheckConfig) { .into_iter() .filter(|krate| { if let Some(only_one_crate) = &config.only { - let name = match krate { - CrateSource::CratesIo { name, .. } - | CrateSource::Git { name, .. } - | CrateSource::Path { name, .. } => name, - }; - - name == only_one_crate + krate.name == *only_one_crate } else { true } From 68aed4a5cebf846d88716489e4ea66074d669c5b Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Wed, 10 Jul 2024 14:32:02 +0200 Subject: [PATCH 061/806] Second byte is not involved in the example; use a Cell<()> instead --- .../tree_borrows/reservedim_spurious_write.rs | 23 +++++++++++-------- .../reservedim_spurious_write.with.stderr | 6 ++--- .../reservedim_spurious_write.without.stderr | 6 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs index 611f64dce5e50..6ae79be6cc7e8 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -41,10 +41,10 @@ macro_rules! synchronized { } fn main() { - // For this example it is important that we have at least two bytes - // because lazyness is involved. - let mut data = [0u8; 2]; - let ptr = SendPtr(std::ptr::addr_of_mut!(data[0])); + // The conflict occurs one one single location but the example involves + // lazily initialized permissions. + let mut data = 0u8; + let ptr = SendPtr(std::ptr::addr_of_mut!(data)); let barrier = Arc::new(Barrier::new(2)); let bx = Arc::clone(&barrier); let by = Arc::clone(&barrier); @@ -84,17 +84,20 @@ fn main() { let ptr = ptr; synchronized!(b, "retag x (&mut, protect)"); synchronized!(b, "[lazy] retag y (&mut, protect, IM)"); - fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { + fn inner(y: &mut Cell<()>, b: IdxBarrier) -> *mut u8 { synchronized!(b, "spurious write x"); synchronized!(b, "ret y"); - y as *mut Cell as *mut u8 + // `y` is not retagged for any bytes, so the pointer we return + // has its permission lazily initialized. + y as *mut Cell<()> as *mut u8 } - // Currently `ptr` points to `data[0]`. We retag it for `data[1]` - // then use it for `data[0]` where its initialization has been deferred. - let y = inner(unsafe { &mut *(ptr.0 as *mut Cell).wrapping_add(1) }, b.clone()); + // Currently `ptr` points to `data`. + // We do a zero-sized retag so that its permission is lazy. + let y_zst = unsafe { &mut *(ptr.0 as *mut Cell<()>) }; + let y = inner(y_zst, b.clone()); synchronized!(b, "ret x"); synchronized!(b, "write y"); - unsafe { *y.wrapping_sub(1) = 13 } //~ERROR: /write access through .* is forbidden/ + unsafe { *y = 13 } //~ERROR: /write access through .* is forbidden/ synchronized!(b, "end"); }); diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr index 5ac9c912ba9ee..0e4517e90105c 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr @@ -15,15 +15,15 @@ Thread 2 executing: write y error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden --> $DIR/reservedim_spurious_write.rs:LL:CC | -LL | unsafe { *y.wrapping_sub(1) = 13 } - | ^^^^^^^^^^^^^^^^^^^^^^^ write access through at ALLOC[0x0] is forbidden +LL | unsafe { *y = 13 } + | ^^^^^^^ write access through at ALLOC[0x0] is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag has state Disabled which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/reservedim_spurious_write.rs:LL:CC | -LL | fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { +LL | fn inner(y: &mut Cell<()>, b: IdxBarrier) -> *mut u8 { | ^ help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1] --> $DIR/reservedim_spurious_write.rs:LL:CC diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr index 97c71fdedefb3..cbeef90243bfb 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr @@ -15,15 +15,15 @@ Thread 2 executing: write y error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden --> $DIR/reservedim_spurious_write.rs:LL:CC | -LL | unsafe { *y.wrapping_sub(1) = 13 } - | ^^^^^^^^^^^^^^^^^^^^^^^ write access through at ALLOC[0x0] is forbidden +LL | unsafe { *y = 13 } + | ^^^^^^^ write access through at ALLOC[0x0] is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag has state Disabled which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/reservedim_spurious_write.rs:LL:CC | -LL | fn inner(y: &mut Cell, b: IdxBarrier) -> *mut u8 { +LL | fn inner(y: &mut Cell<()>, b: IdxBarrier) -> *mut u8 { | ^ help: the accessed tag later transitioned to Disabled due to a protector release (acting as a foreign write access) on every location previously accessed by this tag --> $DIR/reservedim_spurious_write.rs:LL:CC From fe0bd76a8bdb80f4460dd59612076cf91370f7a1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 10 Jul 2024 16:03:20 +0200 Subject: [PATCH 062/806] elaborate unknowable goals if a trait is unknowable, but its super trait is definitely not implemented, then the trait itself is definitely also not implemented. --- .../src/solve/assembly/mod.rs | 12 +++++++ compiler/rustc_type_ir/src/inherent.rs | 1 + .../normalize-for-errors.next.stderr | 1 + tests/ui/coherence/normalize-for-errors.rs | 1 + .../super-trait-knowable-1.current.stderr | 13 ++++++++ .../super-traits/super-trait-knowable-1.rs | 19 +++++++++++ .../super-traits/super-trait-knowable-2.rs | 33 +++++++++++++++++++ .../super-trait-knowable-3.current.stderr | 13 ++++++++ .../super-traits/super-trait-knowable-3.rs | 22 +++++++++++++ ...8728.stderr => issue-48728.current.stderr} | 2 +- tests/ui/issues/issue-48728.rs | 7 +++- 11 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 tests/ui/coherence/super-traits/super-trait-knowable-1.current.stderr create mode 100644 tests/ui/coherence/super-traits/super-trait-knowable-1.rs create mode 100644 tests/ui/coherence/super-traits/super-trait-knowable-2.rs create mode 100644 tests/ui/coherence/super-traits/super-trait-knowable-3.current.stderr create mode 100644 tests/ui/coherence/super-traits/super-trait-knowable-3.rs rename tests/ui/issues/{issue-48728.stderr => issue-48728.current.stderr} (95%) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 01dde9ca587ca..4e1a3558a9c1e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -699,6 +699,18 @@ where if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { Err(NoSolution) } else { + // While the trait bound itself may be unknowable, we may be able to + // prove that a super trait is not implemented. For this, we recursively + // prove the super trait bounds of the current goal. + // + // We skip the goal itself as that one would cycle. + let predicate: I::Predicate = trait_ref.upcast(cx); + ecx.add_goals( + GoalSource::Misc, + elaborate::elaborate(cx, [predicate]) + .skip(1) + .map(|predicate| goal.with(cx, predicate)), + ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } }, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index de86a8536f7af..2b6ccc8e430b5 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -433,6 +433,7 @@ pub trait Predicate>: + UpcastFrom> + UpcastFrom> + IntoKind>> + + Elaboratable { fn as_clause(self) -> Option; diff --git a/tests/ui/coherence/normalize-for-errors.next.stderr b/tests/ui/coherence/normalize-for-errors.next.stderr index 6c56a9177414b..634a10b7a14c3 100644 --- a/tests/ui/coherence/normalize-for-errors.next.stderr +++ b/tests/ui/coherence/normalize-for-errors.next.stderr @@ -7,6 +7,7 @@ LL | LL | impl MyTrait for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(Box<(MyType,)>, <_ as Iterator>::Item)` | + = note: upstream crates may add a new impl of trait `std::clone::Clone` for type `(MyType,)` in future versions = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions error: aborting due to 1 previous error diff --git a/tests/ui/coherence/normalize-for-errors.rs b/tests/ui/coherence/normalize-for-errors.rs index 2288118676ab3..4188389a3ad5b 100644 --- a/tests/ui/coherence/normalize-for-errors.rs +++ b/tests/ui/coherence/normalize-for-errors.rs @@ -18,5 +18,6 @@ impl MyTrait for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {} //~^ ERROR conflicting implementations of trait `MyTrait<_>` for type `(Box<(MyType,)>, //~| NOTE conflicting implementation for `(Box<(MyType,)>, //~| NOTE upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions +//[next]~| NOTE upstream crates may add a new impl of trait `std::clone::Clone` for type `(MyType,)` in future versions fn main() {} diff --git a/tests/ui/coherence/super-traits/super-trait-knowable-1.current.stderr b/tests/ui/coherence/super-traits/super-trait-knowable-1.current.stderr new file mode 100644 index 0000000000000..fb01cf158d980 --- /dev/null +++ b/tests/ui/coherence/super-traits/super-trait-knowable-1.current.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Overlap<_>` for type `()` + --> $DIR/super-trait-knowable-1.rs:16:1 + | +LL | impl> Overlap for U {} + | ----------------------------------- first implementation here +LL | impl Overlap for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + | + = note: downstream crates may implement trait `Sub<_>` for type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/super-traits/super-trait-knowable-1.rs b/tests/ui/coherence/super-traits/super-trait-knowable-1.rs new file mode 100644 index 0000000000000..80df8c19ee51f --- /dev/null +++ b/tests/ui/coherence/super-traits/super-trait-knowable-1.rs @@ -0,0 +1,19 @@ +// Added in #124532. While `(): Super` is knowable, `(): Sub` is not. +// +// We therefore elaborate super trait bounds in the implicit negative +// overlap check. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +trait Super {} +trait Sub: Super {} + +trait Overlap {} +impl> Overlap for U {} +impl Overlap for () {} +//[current]~^ ERROR conflicting implementations + +fn main() {} diff --git a/tests/ui/coherence/super-traits/super-trait-knowable-2.rs b/tests/ui/coherence/super-traits/super-trait-knowable-2.rs new file mode 100644 index 0000000000000..d1f2e8d1c1a15 --- /dev/null +++ b/tests/ui/coherence/super-traits/super-trait-knowable-2.rs @@ -0,0 +1,33 @@ +// A regression test for pyella-0.1.5 which broke when +// enabling the new solver in coherence. +// +// `Tensor: TensorValue` is knowable while `Tensor: TensorOp` +// may be implemented downstream. We previously didn't check the +// super trait bound in coherence, causing these impls to overlap. +// +// However, we did fail to normalize ` {} +pub trait TensorOp: TensorValue {} + +pub struct Tensor; +impl TensorCompare for Tensor {} +impl TensorCompare for T1 +where + T1: TensorOp, + T1::Unmasked: Sized, +{} + + +fn main() {} diff --git a/tests/ui/coherence/super-traits/super-trait-knowable-3.current.stderr b/tests/ui/coherence/super-traits/super-trait-knowable-3.current.stderr new file mode 100644 index 0000000000000..542edb8b7f674 --- /dev/null +++ b/tests/ui/coherence/super-traits/super-trait-knowable-3.current.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Overlap<_>` for type `()` + --> $DIR/super-trait-knowable-3.rs:19:1 + | +LL | impl>> Overlap for U {} + | ---------------------------------------- first implementation here +LL | impl Overlap for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + | + = note: downstream crates may implement trait `Sub<_>` for type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/super-traits/super-trait-knowable-3.rs b/tests/ui/coherence/super-traits/super-trait-knowable-3.rs new file mode 100644 index 0000000000000..295d7ac48d8cc --- /dev/null +++ b/tests/ui/coherence/super-traits/super-trait-knowable-3.rs @@ -0,0 +1,22 @@ +// Unlike in `super-trait-knowable-1.rs`, the knowable +// super trait bound is in a nested goal so this would not +// compile if we were to only elaborate root goals. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +trait Super {} +trait Sub: Super {} + +struct W(T); +trait Bound {} +impl, U> Bound> for T {} + +trait Overlap {} +impl>> Overlap for U {} +impl Overlap for () {} +//[current]~^ ERROR conflicting implementations of trait `Overlap<_>` for type `()` + +fn main() {} diff --git a/tests/ui/issues/issue-48728.stderr b/tests/ui/issues/issue-48728.current.stderr similarity index 95% rename from tests/ui/issues/issue-48728.stderr rename to tests/ui/issues/issue-48728.current.stderr index 6b4247f1d7965..2a1b4ff781854 100644 --- a/tests/ui/issues/issue-48728.stderr +++ b/tests/ui/issues/issue-48728.current.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Clone` for type `Node<[_]>` - --> $DIR/issue-48728.rs:4:10 + --> $DIR/issue-48728.rs:9:10 | LL | #[derive(Clone)] | ^^^^^ conflicting implementation for `Node<[_]>` diff --git a/tests/ui/issues/issue-48728.rs b/tests/ui/issues/issue-48728.rs index cbdc10bd2e1ea..7ef05f4277b27 100644 --- a/tests/ui/issues/issue-48728.rs +++ b/tests/ui/issues/issue-48728.rs @@ -1,7 +1,12 @@ // Regression test for #48728, an ICE that occurred computing // coherence "help" information. -#[derive(Clone)] //~ ERROR conflicting implementations of trait `Clone` +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +#[derive(Clone)] //[current]~ ERROR conflicting implementations of trait `Clone` struct Node(Box); impl Clone for Node<[T]> { From 287b66b0b5cc06b9c0acb01d8a7b0db9ddf764ee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Jun 2024 11:26:10 +0200 Subject: [PATCH 063/806] size_of_val_raw: for length 0 this is safe to call --- library/core/src/alloc/layout.rs | 2 ++ library/core/src/mem/mod.rs | 8 ++++++++ tests/ui/layout/size-of-val-raw-too-big.rs | 18 ++++++++++++++++++ tests/ui/layout/size-of-val-raw-too-big.stderr | 4 ++++ 4 files changed, 32 insertions(+) create mode 100644 tests/ui/layout/size-of-val-raw-too-big.rs create mode 100644 tests/ui/layout/size-of-val-raw-too-big.stderr diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 0b92767c93205..e96a41422a2af 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -183,6 +183,8 @@ impl Layout { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// For the special case where the dynamic tail length is 0, this function + /// is safe to call. /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable for the type `T` acquired by an unsizing coercion, /// and the size of the *entire value* diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index dd4b6e823434e..9bb4ba922cd44 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -359,6 +359,12 @@ pub const fn size_of_val(val: &T) -> usize { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// For the special case where the dynamic tail length is 0, this function +/// is safe to call. +// NOTE: the reason this is safe is that if an overflow were to occur already with size 0, +// then we would stop compilation as even the "statically known" part of the type would +// already be too big (or the call may be in dead code and optimized away, but then it +// doesn't matter). /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable acquired by an unsizing coercion, and the size /// of the *entire value* (dynamic tail length + statically sized prefix) @@ -506,6 +512,8 @@ pub const fn align_of_val(val: &T) -> usize { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// For the special case where the dynamic tail length is 0, this function +/// is safe to call. /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable acquired by an unsizing coercion, and the size /// of the *entire value* (dynamic tail length + statically sized prefix) diff --git a/tests/ui/layout/size-of-val-raw-too-big.rs b/tests/ui/layout/size-of-val-raw-too-big.rs new file mode 100644 index 0000000000000..8d82c78d95397 --- /dev/null +++ b/tests/ui/layout/size-of-val-raw-too-big.rs @@ -0,0 +1,18 @@ +//@ build-fail +//@ compile-flags: --crate-type lib +//@ only-32bit Layout computation rejects this layout for different reasons on 64-bit. +//@ error-pattern: too big for the current architecture +#![feature(core_intrinsics)] +#![allow(internal_features)] + +// isize::MAX is fine, but with the padding for the unsized tail it is too big. +#[repr(C)] +pub struct Example([u8; isize::MAX as usize], [u16]); + +// We guarantee that with length 0, `size_of_val_raw` (which calls the `size_of_val` intrinsic) +// is safe to call. The compiler aborts compilation if a length of 0 would overflow. +// So let's construct a case where length 0 just barely overflows, and ensure that +// does abort compilation. +pub fn check(x: *const Example) -> usize { + unsafe { std::intrinsics::size_of_val(x) } +} diff --git a/tests/ui/layout/size-of-val-raw-too-big.stderr b/tests/ui/layout/size-of-val-raw-too-big.stderr new file mode 100644 index 0000000000000..aa9abd644faaf --- /dev/null +++ b/tests/ui/layout/size-of-val-raw-too-big.stderr @@ -0,0 +1,4 @@ +error: values of the type `Example` are too big for the current architecture + +error: aborting due to 1 previous error + From 0d49862998b3695297dffba21c80b35fb73e245d Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 10 Jul 2024 13:05:03 -0500 Subject: [PATCH 064/806] Clarify/add `must_use` messages for more `into_raw*` functions of `alloc` types. --- library/alloc/src/boxed.rs | 2 ++ library/alloc/src/rc.rs | 2 ++ library/alloc/src/string.rs | 2 +- library/alloc/src/vec/mod.rs | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 65bcb241e4aec..21fd8d24af3d7 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1097,6 +1097,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Self) -> *mut T { @@ -1150,6 +1151,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 3745ecb48c18e..af3ace96d9c29 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1372,6 +1372,7 @@ impl Rc { /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; /// assert_eq!(&*x, "hello"); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); @@ -3100,6 +3101,7 @@ impl Weak { /// /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(self) -> (*const T, A) { diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 36078da7c35a6..ede2a42d12fe5 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -903,7 +903,7 @@ impl String { /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; /// assert_eq!(rebuilt, "hello"); /// ``` - #[must_use = "`self` will be dropped if the result is not used"] + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 6e9b017ad75cf..b01ccb3848341 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -879,6 +879,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); @@ -922,6 +923,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { From 6d477d3a9de714d877a9eda48b0eda3e12e301be Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 10 Jul 2024 13:08:24 -0500 Subject: [PATCH 065/806] Add `must_use` to IntoRawFd/IntoRawSocket/IntoRawHandle's methods. --- library/std/src/os/fd/raw.rs | 1 + library/std/src/os/solid/io.rs | 1 + library/std/src/os/windows/io/raw.rs | 2 ++ 3 files changed, 4 insertions(+) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index ef896ea95c9c9..9d6fcaa3634d8 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -138,6 +138,7 @@ pub trait IntoRawFd { /// let raw_fd: RawFd = f.into_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` + #[must_use = "losing the raw file descriptor may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 19b4fe22093c3..13d8419830df1 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -347,6 +347,7 @@ pub trait IntoRawFd { /// This function **transfers ownership** of the underlying file descriptor /// to the caller. Callers are then the unique owners of the file descriptor /// and must close the descriptor once it's no longer needed. + #[must_use = "losing the raw file descriptor may leak resources"] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 770583a9ce3e0..e76650be742a6 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -89,6 +89,7 @@ pub trait IntoRawHandle { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw handle may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_handle(self) -> RawHandle; } @@ -230,6 +231,7 @@ pub trait IntoRawSocket { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw socket may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_socket(self) -> RawSocket; } From e8445818d49fe968b35a130c66071c1b56498f93 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 27 Jun 2024 12:58:35 -0400 Subject: [PATCH 066/806] Reorder modifiers and polarity to be *after* binder in trait bounds --- compiler/rustc_parse/src/parser/ty.rs | 4 +- .../async-fn/higher-ranked-async-fn.rs | 2 +- .../impl-trait/normalize-tait-in-const.stderr | 41 ++----------------- tests/ui/issues/issue-39089.rs | 3 +- tests/ui/issues/issue-39089.stderr | 8 ++++ tests/ui/parser/bounds-type.rs | 2 +- .../tilde-const-syntax.rs | 4 +- 7 files changed, 19 insertions(+), 45 deletions(-) create mode 100644 tests/ui/issues/issue-39089.stderr diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 1e5b227aaa9be..24183ac1218e9 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -978,7 +978,7 @@ impl<'a> Parser<'a> { /// Parses a type bound according to: /// ```ebnf /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) - /// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for] SIMPLE_PATH + /// TY_BOUND_NOPAREN = [for] [TRAIT_BOUND_MODIFIERS] SIMPLE_PATH /// ``` /// /// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`. @@ -988,8 +988,8 @@ impl<'a> Parser<'a> { has_parens: bool, leading_token: &Token, ) -> PResult<'a, GenericBound> { - let modifiers = self.parse_trait_bound_modifiers()?; let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?; + let modifiers = self.parse_trait_bound_modifiers()?; // Recover erroneous lifetime bound with modifiers or binder. // e.g. `T: for<'a> 'a` or `T: ~const 'a`. diff --git a/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs index be338ddeb7dbf..f8da517213aee 100644 --- a/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs +++ b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs @@ -15,7 +15,7 @@ async fn f(arg: &i32) {} async fn func(f: F) where - F: async for<'a> Fn(&'a i32), + F: for<'a> async Fn(&'a i32), { let x: i32 = 0; f(&x).await; diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index 73f4d4c388563..77de689fb97a8 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -1,41 +1,8 @@ -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/normalize-tait-in-const.rs:27:42 +error: expected a trait, found type + --> $DIR/normalize-tait-in-const.rs:27:34 | LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/normalize-tait-in-const.rs:27:69 - | -LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^^^^^^ - -error[E0015]: cannot call non-const closure in constant functions - --> $DIR/normalize-tait-in-const.rs:28:5 - | -LL | fun(filter_positive()); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -help: consider further restricting this bound - | -LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct + ~const Fn(&foo::Alias<'_>)>(fun: F) { - | ++++++++++++++++++++++++++++ -help: add `#![feature(effects)]` to the crate attributes to enable - | -LL + #![feature(effects)] - | - -error[E0493]: destructor of `F` cannot be evaluated at compile-time - --> $DIR/normalize-tait-in-const.rs:27:79 - | -LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^ the destructor for this type cannot be evaluated in constant functions -LL | fun(filter_positive()); -LL | } - | - value is dropped here - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0015, E0493. -For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/issues/issue-39089.rs b/tests/ui/issues/issue-39089.rs index b00b842380235..e6bec33735498 100644 --- a/tests/ui/issues/issue-39089.rs +++ b/tests/ui/issues/issue-39089.rs @@ -1,5 +1,4 @@ -//@ check-pass -#![allow(dead_code)] fn f Sized>() {} +//~^ ERROR expected a trait, found type fn main() {} diff --git a/tests/ui/issues/issue-39089.stderr b/tests/ui/issues/issue-39089.stderr new file mode 100644 index 0000000000000..3e57a6fcbcb5a --- /dev/null +++ b/tests/ui/issues/issue-39089.stderr @@ -0,0 +1,8 @@ +error: expected a trait, found type + --> $DIR/issue-39089.rs:1:10 + | +LL | fn f Sized>() {} + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/bounds-type.rs b/tests/ui/parser/bounds-type.rs index a1971fa3146d1..2b2a44b706282 100644 --- a/tests/ui/parser/bounds-type.rs +++ b/tests/ui/parser/bounds-type.rs @@ -5,7 +5,7 @@ struct S< T: Tr + 'a, // OK T: 'a, // OK T:, // OK - T: ?for<'a> Trait, // OK + T: for<'a> ?Trait, // OK T: Tr +, // OK T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs index 496f97b5e24aa..4dd0e69598d72 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs @@ -4,6 +4,6 @@ #![feature(const_trait_impl)] struct S< - T: ~const ?for<'a> Tr<'a> + 'static + ~const std::ops::Add, - T: ~const ?for<'a: 'b> m::Trait<'a>, + T: for<'a> ~const ?Tr<'a> + 'static + ~const std::ops::Add, + T: for<'a: 'b> ~const ?m::Trait<'a>, >; From 32c8bfdb11e519c6608ead730b6dfafc6cafb9c5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 27 Jun 2024 13:07:03 -0400 Subject: [PATCH 067/806] Improve error message --- compiler/rustc_parse/messages.ftl | 3 ++ compiler/rustc_parse/src/errors.rs | 9 ++++ compiler/rustc_parse/src/parser/ty.rs | 8 ++++ src/tools/tidy/src/ui_tests.rs | 2 +- .../ui/impl-trait/normalize-tait-in-const.rs | 2 +- .../impl-trait/normalize-tait-in-const.stderr | 43 ++++++++++++++++--- tests/ui/issues/issue-39089.rs | 2 +- tests/ui/issues/issue-39089.stderr | 8 ++-- 8 files changed, 66 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 02c3c87313bcd..5fb59eeb4f323 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -53,6 +53,9 @@ parse_bare_cr = {$double_quotes -> parse_bare_cr_in_raw_string = bare CR not allowed in raw string +parse_binder_before_modifiers = `for<...>` binder should be placed before trait bound modifiers + .label = place the `for<...>` binder before any modifiers + parse_bounds_not_allowed_on_trait_aliases = bounds are not allowed on trait aliases parse_box_not_pat = expected pattern, found {$descr} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 3ae9b6dad998d..6738cc4a12004 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3041,3 +3041,12 @@ pub struct UnsafeAttrOutsideUnsafeSuggestion { #[suggestion_part(code = ")")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(parse_binder_before_modifiers)] +pub struct BinderBeforeModifiers { + #[primary_span] + pub binder_span: Span, + #[label] + pub modifiers_span: Span, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 24183ac1218e9..306029ca94c6b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -989,7 +989,10 @@ impl<'a> Parser<'a> { leading_token: &Token, ) -> PResult<'a, GenericBound> { let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?; + + let modifiers_lo = self.token.span; let modifiers = self.parse_trait_bound_modifiers()?; + let modifiers_span = modifiers_lo.to(self.prev_token.span); // Recover erroneous lifetime bound with modifiers or binder. // e.g. `T: for<'a> 'a` or `T: ~const 'a`. @@ -998,6 +1001,11 @@ impl<'a> Parser<'a> { return self.parse_generic_lt_bound(lo, has_parens); } + if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? { + lifetime_defs.extend(more_lifetime_defs); + self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span }); + } + let mut path = if self.token.is_keyword(kw::Fn) && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis)) && let Some(path) = self.recover_path_from_fn() diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 5e6992038e395..0ae0356b2c452 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -16,7 +16,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: u32 = 901; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: u32 = 1672; +const ISSUES_ENTRY_LIMIT: u32 = 1673; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/ui/impl-trait/normalize-tait-in-const.rs b/tests/ui/impl-trait/normalize-tait-in-const.rs index fc90139d64095..e3f53e5f8a82a 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.rs +++ b/tests/ui/impl-trait/normalize-tait-in-const.rs @@ -24,7 +24,7 @@ mod foo { } use foo::*; -const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { +const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { fun(filter_positive()); } diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index 77de689fb97a8..b20dabe7b25ac 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -1,8 +1,41 @@ -error: expected a trait, found type - --> $DIR/normalize-tait-in-const.rs:27:34 +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/normalize-tait-in-const.rs:27:42 | -LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/normalize-tait-in-const.rs:27:69 + | +LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { + | ^^^^^^^^ + +error[E0015]: cannot call non-const closure in constant functions + --> $DIR/normalize-tait-in-const.rs:28:5 + | +LL | fun(filter_positive()); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants +help: consider further restricting this bound + | +LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct + ~const Fn(&foo::Alias<'_>)>(fun: F) { + | ++++++++++++++++++++++++++++ +help: add `#![feature(effects)]` to the crate attributes to enable + | +LL + #![feature(effects)] + | + +error[E0493]: destructor of `F` cannot be evaluated at compile-time + --> $DIR/normalize-tait-in-const.rs:27:79 + | +LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { + | ^^^ the destructor for this type cannot be evaluated in constant functions +LL | fun(filter_positive()); +LL | } + | - value is dropped here + +error: aborting due to 4 previous errors +Some errors have detailed explanations: E0015, E0493. +For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/issues/issue-39089.rs b/tests/ui/issues/issue-39089.rs index e6bec33735498..822c47503afe9 100644 --- a/tests/ui/issues/issue-39089.rs +++ b/tests/ui/issues/issue-39089.rs @@ -1,4 +1,4 @@ fn f Sized>() {} -//~^ ERROR expected a trait, found type +//~^ ERROR `for<...>` binder should be placed before trait bound modifiers fn main() {} diff --git a/tests/ui/issues/issue-39089.stderr b/tests/ui/issues/issue-39089.stderr index 3e57a6fcbcb5a..a81010aedff5a 100644 --- a/tests/ui/issues/issue-39089.stderr +++ b/tests/ui/issues/issue-39089.stderr @@ -1,8 +1,10 @@ -error: expected a trait, found type - --> $DIR/issue-39089.rs:1:10 +error: `for<...>` binder should be placed before trait bound modifiers + --> $DIR/issue-39089.rs:1:13 | LL | fn f Sized>() {} - | ^^^^^^^^^^^^^ + | - ^^^^ + | | + | place the `for<...>` binder before any modifiers error: aborting due to 1 previous error From 898ed2ffa6c485530af1fbe6117c0deb4290715f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 10 Jul 2024 17:49:50 -0400 Subject: [PATCH 068/806] Enforce that ? and for<...> are not combined --- compiler/rustc_parse/messages.ftl | 3 +++ compiler/rustc_parse/src/errors.rs | 10 ++++++++++ compiler/rustc_parse/src/parser/ty.rs | 13 +++++++++++++ tests/ui/parser/bounds-type.rs | 2 +- tests/ui/parser/bounds-type.stderr | 10 +++++++++- .../rfc-2632-const-trait-impl/tilde-const-syntax.rs | 4 ++-- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 5fb59eeb4f323..5b4a5f4dd38e0 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -53,6 +53,9 @@ parse_bare_cr = {$double_quotes -> parse_bare_cr_in_raw_string = bare CR not allowed in raw string +parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier + .label = there is not a well-defined meaning for a higher-ranked `{$polarity}` trait + parse_binder_before_modifiers = `for<...>` binder should be placed before trait bound modifiers .label = place the `for<...>` binder before any modifiers diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6738cc4a12004..9b18a771fdea4 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3050,3 +3050,13 @@ pub struct BinderBeforeModifiers { #[label] pub modifiers_span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_binder_and_polarity)] +pub struct BinderAndPolarity { + #[primary_span] + pub polarity_span: Span, + #[label] + pub binder_span: Span, + pub polarity: &'static str, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 306029ca94c6b..6de778fa9f21f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -994,6 +994,19 @@ impl<'a> Parser<'a> { let modifiers = self.parse_trait_bound_modifiers()?; let modifiers_span = modifiers_lo.to(self.prev_token.span); + if let Some(binder_span) = binder_span { + match modifiers.polarity { + BoundPolarity::Negative(polarity_span) | BoundPolarity::Maybe(polarity_span) => { + self.dcx().emit_err(errors::BinderAndPolarity { + binder_span, + polarity_span, + polarity: modifiers.polarity.as_str(), + }); + } + BoundPolarity::Positive => {} + } + } + // Recover erroneous lifetime bound with modifiers or binder. // e.g. `T: for<'a> 'a` or `T: ~const 'a`. if self.token.is_lifetime() { diff --git a/tests/ui/parser/bounds-type.rs b/tests/ui/parser/bounds-type.rs index 2b2a44b706282..47fec7b1e77f9 100644 --- a/tests/ui/parser/bounds-type.rs +++ b/tests/ui/parser/bounds-type.rs @@ -5,7 +5,7 @@ struct S< T: Tr + 'a, // OK T: 'a, // OK T:, // OK - T: for<'a> ?Trait, // OK + T: for<'a> ?Trait, //~ ERROR `for<...>` binder not allowed with `?` trait polarity modifier T: Tr +, // OK T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds diff --git a/tests/ui/parser/bounds-type.stderr b/tests/ui/parser/bounds-type.stderr index d1210e88d6678..fa8ab6812b156 100644 --- a/tests/ui/parser/bounds-type.stderr +++ b/tests/ui/parser/bounds-type.stderr @@ -1,3 +1,11 @@ +error: `for<...>` binder not allowed with `?` trait polarity modifier + --> $DIR/bounds-type.rs:8:16 + | +LL | T: for<'a> ?Trait, + | ---- ^ + | | + | there is not a well-defined meaning for a higher-ranked `?` trait + error: `?` may only modify trait bounds, not lifetime bounds --> $DIR/bounds-type.rs:10:8 | @@ -16,5 +24,5 @@ error: `const` may only modify trait bounds, not lifetime bounds LL | T: const 'a, | ^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs index 4dd0e69598d72..d65ecae3d067c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs @@ -4,6 +4,6 @@ #![feature(const_trait_impl)] struct S< - T: for<'a> ~const ?Tr<'a> + 'static + ~const std::ops::Add, - T: for<'a: 'b> ~const ?m::Trait<'a>, + T: for<'a> ~const Tr<'a> + 'static + ~const std::ops::Add, + T: for<'a: 'b> ~const m::Trait<'a>, >; From de560c30659281e8c91a48579f907097efe22adf Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 20 Feb 2024 21:40:25 +0100 Subject: [PATCH 069/806] Implement `ambiguous_negative_literals` lint --- compiler/rustc_lint/messages.ftl | 5 + compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 29 ++++ compiler/rustc_lint/src/precedence.rs | 70 ++++++++++ tests/ui/lint/negative_literals.rs | 35 +++++ tests/ui/lint/negative_literals.stderr | 179 +++++++++++++++++++++++++ 6 files changed, 321 insertions(+) create mode 100644 compiler/rustc_lint/src/precedence.rs create mode 100644 tests/ui/lint/negative_literals.rs create mode 100644 tests/ui/lint/negative_literals.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f048f6fe8ad4b..4f7f01e56b63d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -5,6 +5,11 @@ lint_ambiguous_glob_reexport = ambiguous glob re-exports .label_first_reexport = the name `{$name}` in the {$namespace} namespace is first re-exported here .label_duplicate_reexport = but the name `{$name}` in the {$namespace} namespace is also re-exported here +lint_ambiguous_negative_literals = `-` has lower precedence than method calls, which might be unexpected + .example = e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` + .negative_literal = add parentheses around the `-` and the literal to call the method on a negative literal + .current_behavior = add parentheses around the literal and the method call to keep the current behavior + lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 868a44a980a0f..0a058b675ffa5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -72,6 +72,7 @@ mod noop_method_call; mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; +mod precedence; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; @@ -109,6 +110,7 @@ use nonstandard_style::*; use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; +use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; @@ -173,6 +175,7 @@ early_lint_methods!( RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, Expr2024: Expr2024, + Precedence: Precedence, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 54c73710eca6f..873322c13b46d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1489,6 +1489,35 @@ pub struct NonLocalDefinitionsCargoUpdateNote { pub crate_name: Symbol, } +// precedence.rs +#[derive(LintDiagnostic)] +#[diag(lint_ambiguous_negative_literals)] +#[note(lint_example)] +pub struct AmbiguousNegativeLiteralsDiag { + #[subdiagnostic] + pub negative_literal: AmbiguousNegativeLiteralsNegativeLiteralSuggestion, + #[subdiagnostic] + pub current_behavior: AmbiguousNegativeLiteralsCurrentBehaviorSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_negative_literal, applicability = "maybe-incorrect")] +pub struct AmbiguousNegativeLiteralsNegativeLiteralSuggestion { + #[suggestion_part(code = "(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_current_behavior, applicability = "maybe-incorrect")] +pub struct AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { + #[suggestion_part(code = "(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + // pass_by_value.rs #[derive(LintDiagnostic)] #[diag(lint_pass_by_value)] diff --git a/compiler/rustc_lint/src/precedence.rs b/compiler/rustc_lint/src/precedence.rs new file mode 100644 index 0000000000000..eb2ba39727797 --- /dev/null +++ b/compiler/rustc_lint/src/precedence.rs @@ -0,0 +1,70 @@ +use rustc_ast::token::LitKind; +use rustc_ast::{Expr, ExprKind, MethodCall, UnOp}; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{ + AmbiguousNegativeLiteralsCurrentBehaviorSuggestion, AmbiguousNegativeLiteralsDiag, + AmbiguousNegativeLiteralsNegativeLiteralSuggestion, +}; +use crate::{EarlyContext, EarlyLintPass, LintContext}; + +declare_lint! { + /// The `ambiguous_negative_literals` lint checks for cases that are + /// confusing between a negative literal and a negation that's not part + /// of the literal. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1 + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Method calls take precedence over unary precedence. Setting the + /// precedence explicitly makes the code clearer and avoid potential bugs. + pub AMBIGUOUS_NEGATIVE_LITERALS, + Deny, + "ambiguous negative literals operations", + report_in_external_macro +} + +declare_lint_pass!(Precedence => [AMBIGUOUS_NEGATIVE_LITERALS]); + +impl EarlyLintPass for Precedence { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind else { + return; + }; + + let mut arg = operand; + let mut at_least_one = false; + while let ExprKind::MethodCall(box MethodCall { receiver, .. }) = &arg.kind { + at_least_one = true; + arg = receiver; + } + + if at_least_one + && let ExprKind::Lit(lit) = &arg.kind + && let LitKind::Integer | LitKind::Float = &lit.kind + { + cx.emit_span_lint( + AMBIGUOUS_NEGATIVE_LITERALS, + expr.span, + AmbiguousNegativeLiteralsDiag { + negative_literal: AmbiguousNegativeLiteralsNegativeLiteralSuggestion { + start_span: expr.span.shrink_to_lo(), + end_span: arg.span.shrink_to_hi(), + }, + current_behavior: AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { + start_span: operand.span.shrink_to_lo(), + end_span: operand.span.shrink_to_hi(), + }, + }, + ); + } + } +} diff --git a/tests/ui/lint/negative_literals.rs b/tests/ui/lint/negative_literals.rs new file mode 100644 index 0000000000000..048fcd6ff57bc --- /dev/null +++ b/tests/ui/lint/negative_literals.rs @@ -0,0 +1,35 @@ +//@ check-fail + +fn main() { + let _ = -1i32.abs(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f32.abs(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f64.asin(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f64.asinh(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f64.tan(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f64.tanh(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1.0_f64.cos().cos(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1.0_f64.cos().sin(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1.0_f64.sin().cos(); + //~^ ERROR `-` has lower precedence than method calls + let _ = -1f64.sin().sin(); + //~^ ERROR `-` has lower precedence than method calls + + dbg!( -1.0_f32.cos() ); + //~^ ERROR `-` has lower precedence than method calls + + // should not warn + let _ = (-1i32).abs(); + let _ = (-1f32).abs(); + let _ = -(1i32).abs(); + let _ = -(1f32).abs(); + let _ = -(1i32.abs()); + let _ = -(1f32.abs()); +} diff --git a/tests/ui/lint/negative_literals.stderr b/tests/ui/lint/negative_literals.stderr new file mode 100644 index 0000000000000..df000a7188255 --- /dev/null +++ b/tests/ui/lint/negative_literals.stderr @@ -0,0 +1,179 @@ +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:4:13 + | +LL | let _ = -1i32.abs(); + | ^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` + = note: `#[deny(ambiguous_negative_literals)]` on by default +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1i32).abs(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1i32.abs()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:6:13 + | +LL | let _ = -1f32.abs(); + | ^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f32).abs(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f32.abs()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:8:13 + | +LL | let _ = -1f64.asin(); + | ^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f64).asin(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f64.asin()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:10:13 + | +LL | let _ = -1f64.asinh(); + | ^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f64).asinh(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f64.asinh()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:12:13 + | +LL | let _ = -1f64.tan(); + | ^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f64).tan(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f64.tan()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:14:13 + | +LL | let _ = -1f64.tanh(); + | ^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f64).tanh(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f64.tanh()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:16:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1.0_f64).cos().cos(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1.0_f64.cos().cos()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:18:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1.0_f64).cos().sin(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1.0_f64.cos().sin()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:20:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1.0_f64).sin().cos(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1.0_f64.sin().cos()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:22:13 + | +LL | let _ = -1f64.sin().sin(); + | ^^^^^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | let _ = (-1f64).sin().sin(); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | let _ = -(1f64.sin().sin()); + | + + + +error: `-` has lower precedence than method calls, which might be unexpected + --> $DIR/negative_literals.rs:25:11 + | +LL | dbg!( -1.0_f32.cos() ); + | ^^^^^^^^^^^^^^ + | + = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` +help: add parentheses around the `-` and the literal to call the method on a negative literal + | +LL | dbg!( (-1.0_f32).cos() ); + | + + +help: add parentheses around the literal and the method call to keep the current behavior + | +LL | dbg!( -(1.0_f32.cos()) ); + | + + + +error: aborting due to 11 previous errors + From c5e1a12efc32b4e487a8899b143909e17d864adf Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 20 Feb 2024 22:19:26 +0100 Subject: [PATCH 070/806] Remove unary neg from `clippy::precedence` lint --- .../clippy/clippy_lints/src/precedence.rs | 56 +------------------ src/tools/clippy/tests/ui/precedence.fixed | 34 ----------- src/tools/clippy/tests/ui/precedence.rs | 34 ----------- src/tools/clippy/tests/ui/precedence.stderr | 32 +---------- .../clippy/tests/ui/unnecessary_cast.fixed | 2 +- src/tools/clippy/tests/ui/unnecessary_cast.rs | 2 +- 6 files changed, 4 insertions(+), 156 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index ff83725da6913..37f5dd5583bfb 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,38 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; -use rustc_ast::token; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -const ALLOWED_ODD_FUNCTIONS: [&str; 14] = [ - "asin", - "asinh", - "atan", - "atanh", - "cbrt", - "fract", - "round", - "signum", - "sin", - "sinh", - "tan", - "tanh", - "to_degrees", - "to_radians", -]; - declare_clippy_lint! { /// ### What it does /// Checks for operations where precedence may be unclear /// and suggests to add parentheses. Currently it catches the following: /// * mixed usage of arithmetic and bit shifting/combining operators without /// parentheses - /// * a "negative" numeric literal (which is really a unary `-` followed by a - /// numeric literal) - /// followed by a method call /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -41,7 +20,6 @@ declare_clippy_lint! { /// /// ### Example /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 - /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1 #[clippy::version = "pre 1.29.0"] pub PRECEDENCE, complexity, @@ -104,38 +82,6 @@ impl EarlyLintPass for Precedence { (false, false) => (), } } - - if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { - let mut arg = operand; - - let mut all_odd = true; - while let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &arg.kind { - let seg_str = seg.ident.name.as_str(); - all_odd &= ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *seg_str); - arg = receiver; - } - - if !all_odd - && let ExprKind::Lit(lit) = &arg.kind - && let token::LitKind::Integer | token::LitKind::Float = &lit.kind - { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, operand.span, "..", &mut applicability) - ), - applicability, - ); - } - } } } diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index cc87de0d90f18..c25c2062aceba 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -20,40 +20,6 @@ fn main() { 1 ^ (1 - 1); 3 | (2 - 1); 3 & (5 - 2); - -(1i32.abs()); - -(1f32.abs()); - - // These should not trigger an error - let _ = (-1i32).abs(); - let _ = (-1f32).abs(); - let _ = -(1i32).abs(); - let _ = -(1f32).abs(); - let _ = -(1i32.abs()); - let _ = -(1f32.abs()); - - // Odd functions should not trigger an error - let _ = -1f64.asin(); - let _ = -1f64.asinh(); - let _ = -1f64.atan(); - let _ = -1f64.atanh(); - let _ = -1f64.cbrt(); - let _ = -1f64.fract(); - let _ = -1f64.round(); - let _ = -1f64.signum(); - let _ = -1f64.sin(); - let _ = -1f64.sinh(); - let _ = -1f64.tan(); - let _ = -1f64.tanh(); - let _ = -1f64.to_degrees(); - let _ = -1f64.to_radians(); - - // Chains containing any non-odd function should trigger (issue #5924) - let _ = -(1.0_f64.cos().cos()); - let _ = -(1.0_f64.cos().sin()); - let _ = -(1.0_f64.sin().cos()); - - // Chains of odd functions shouldn't trigger - let _ = -1f64.sin().sin(); let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs index 00c18d92b5fb6..dc242ecf4c72e 100644 --- a/src/tools/clippy/tests/ui/precedence.rs +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -20,40 +20,6 @@ fn main() { 1 ^ 1 - 1; 3 | 2 - 1; 3 & 5 - 2; - -1i32.abs(); - -1f32.abs(); - - // These should not trigger an error - let _ = (-1i32).abs(); - let _ = (-1f32).abs(); - let _ = -(1i32).abs(); - let _ = -(1f32).abs(); - let _ = -(1i32.abs()); - let _ = -(1f32.abs()); - - // Odd functions should not trigger an error - let _ = -1f64.asin(); - let _ = -1f64.asinh(); - let _ = -1f64.atan(); - let _ = -1f64.atanh(); - let _ = -1f64.cbrt(); - let _ = -1f64.fract(); - let _ = -1f64.round(); - let _ = -1f64.signum(); - let _ = -1f64.sin(); - let _ = -1f64.sinh(); - let _ = -1f64.tan(); - let _ = -1f64.tanh(); - let _ = -1f64.to_degrees(); - let _ = -1f64.to_radians(); - - // Chains containing any non-odd function should trigger (issue #5924) - let _ = -1.0_f64.cos().cos(); - let _ = -1.0_f64.cos().sin(); - let _ = -1.0_f64.sin().cos(); - - // Chains of odd functions shouldn't trigger - let _ = -1f64.sin().sin(); let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 47e61326219d9..8057c25a5e499 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -43,35 +43,5 @@ error: operator precedence can trip the unwary LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: unary minus has lower precedence than method call - --> tests/ui/precedence.rs:23:5 - | -LL | -1i32.abs(); - | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())` - -error: unary minus has lower precedence than method call - --> tests/ui/precedence.rs:24:5 - | -LL | -1f32.abs(); - | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` - -error: unary minus has lower precedence than method call - --> tests/ui/precedence.rs:51:13 - | -LL | let _ = -1.0_f64.cos().cos(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` - -error: unary minus has lower precedence than method call - --> tests/ui/precedence.rs:52:13 - | -LL | let _ = -1.0_f64.cos().sin(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` - -error: unary minus has lower precedence than method call - --> tests/ui/precedence.rs:53:13 - | -LL | let _ = -1.0_f64.sin().cos(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` - -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index 288541362cddb..c43e50761bd52 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -206,7 +206,7 @@ mod fixable { fn issue_9563() { let _: f64 = (-8.0_f64).exp(); - #[allow(clippy::precedence)] + #[allow(ambiguous_negative_literals)] let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index eef3a42e35149..4a5ca231315eb 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -206,7 +206,7 @@ mod fixable { fn issue_9563() { let _: f64 = (-8.0 as f64).exp(); - #[allow(clippy::precedence)] + #[allow(ambiguous_negative_literals)] let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior } From 84d84daf1735d8996cdaf3aea487051215cffdf3 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 10 Jul 2024 21:03:25 -0500 Subject: [PATCH 071/806] Explicitly ignore `into_raw_handle()` using `let _ =` in sys/pal/windows. --- library/std/src/sys/pal/windows/process.rs | 2 +- library/std/src/sys/pal/windows/stdio.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index c62764696b86b..6b3b79149ce21 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -571,7 +571,7 @@ impl Stdio { Ok(io) => unsafe { let io = Handle::from_raw_handle(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw_handle(); + let _ = io.into_raw_handle(); // Don't close the handle ret }, // If no stdio handle is available, then propagate the null value. diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 10aeeac07ea2e..88b3996466fff 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -101,7 +101,7 @@ fn write( unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } @@ -250,7 +250,7 @@ impl io::Read for Stdin { unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.read(buf); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } From de88bc5c8921774681ecac0a5249b370a9768a6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 10 Jul 2024 18:06:44 -0400 Subject: [PATCH 072/806] And additionally enforce ? and async/const aren't mixed --- compiler/rustc_ast_passes/messages.ftl | 2 - .../rustc_ast_passes/src/ast_validation.rs | 11 ----- compiler/rustc_ast_passes/src/errors.rs | 9 ---- compiler/rustc_parse/messages.ftl | 3 ++ compiler/rustc_parse/src/errors.rs | 11 +++++ compiler/rustc_parse/src/parser/ty.rs | 29 ++++++++++++ tests/ui/parser/bounds-type.rs | 13 +++++- tests/ui/parser/bounds-type.stderr | 46 +++++++++++++++++-- ...utually-exclusive-trait-bound-modifiers.rs | 8 ++-- ...lly-exclusive-trait-bound-modifiers.stderr | 32 ++++++++----- 10 files changed, 120 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 7da726ef40868..0408f6ad54359 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -152,8 +152,6 @@ ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed .help = remove one of these features -ast_passes_incompatible_trait_bound_modifiers = `{$left}` and `{$right}` are mutually exclusive - ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation} .because = {$annotation} because of this .type = inherent impl for this type diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index dd0d904c52cc2..8041b89abe258 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1435,17 +1435,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }; self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); } - ( - _, - BoundConstness::Always(_) | BoundConstness::Maybe(_), - BoundPolarity::Negative(_) | BoundPolarity::Maybe(_), - ) => { - self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers { - span: bound.span(), - left: modifiers.constness.as_str(), - right: modifiers.polarity.as_str(), - }); - } _ => {} } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index bfb9047645011..51f80b9ad4acb 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -657,15 +657,6 @@ pub enum TildeConstReason { Item, } -#[derive(Diagnostic)] -#[diag(ast_passes_incompatible_trait_bound_modifiers)] -pub struct IncompatibleTraitBoundModifiers { - #[primary_span] - pub span: Span, - pub left: &'static str, - pub right: &'static str, -} - #[derive(Diagnostic)] #[diag(ast_passes_const_and_async)] pub struct ConstAndAsync { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 5b4a5f4dd38e0..f0cdb6c63a498 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -575,6 +575,9 @@ parse_missing_trait_in_trait_impl = missing trait in a trait impl parse_modifier_lifetime = `{$modifier}` may only modify trait bounds, not lifetime bounds .suggestion = remove the `{$modifier}` +parse_modifiers_and_polarity = `{$modifiers_concatenated}` trait not allowed with `{$polarity}` trait polarity modifier + .label = there is not a well-defined meaning for a `{$modifiers_concatenated} {$polarity}` trait + parse_more_than_one_char = character literal may only contain one codepoint .followed_by = this `{$chr}` is followed by the combining {$len -> [one] mark diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 9b18a771fdea4..75417885d2aff 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3060,3 +3060,14 @@ pub struct BinderAndPolarity { pub binder_span: Span, pub polarity: &'static str, } + +#[derive(Diagnostic)] +#[diag(parse_modifiers_and_polarity)] +pub struct PolarityAndModifiers { + #[primary_span] + pub polarity_span: Span, + #[label] + pub modifiers_span: Span, + pub polarity: &'static str, + pub modifiers_concatenated: String, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6de778fa9f21f..a2db4b6feef77 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -930,6 +930,7 @@ impl<'a> Parser<'a> { /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"] /// ``` fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> { + let modifier_lo = self.token.span; let constness = if self.eat(&token::Tilde) { let tilde = self.prev_token.span; self.expect_keyword(kw::Const)?; @@ -962,6 +963,7 @@ impl<'a> Parser<'a> { } else { BoundAsyncness::Normal }; + let modifier_hi = self.prev_token.span; let polarity = if self.eat(&token::Question) { BoundPolarity::Maybe(self.prev_token.span) @@ -972,6 +974,33 @@ impl<'a> Parser<'a> { BoundPolarity::Positive }; + // Enforce the mutual-exclusivity of `const`/`async` and `?`/`!`. + match polarity { + BoundPolarity::Positive => { + // All trait bound modifiers allowed to combine with positive polarity + } + BoundPolarity::Maybe(polarity_span) | BoundPolarity::Negative(polarity_span) => { + match (asyncness, constness) { + (BoundAsyncness::Normal, BoundConstness::Never) => { + // Ok, no modifiers. + } + (_, _) => { + let constness = constness.as_str(); + let asyncness = asyncness.as_str(); + let glue = + if !constness.is_empty() && !asyncness.is_empty() { " " } else { "" }; + let modifiers_concatenated = format!("{constness}{glue}{asyncness}"); + self.dcx().emit_err(errors::PolarityAndModifiers { + polarity_span, + polarity: polarity.as_str(), + modifiers_span: modifier_lo.to(modifier_hi), + modifiers_concatenated, + }); + } + } + } + } + Ok(TraitBoundModifiers { constness, asyncness, polarity }) } diff --git a/tests/ui/parser/bounds-type.rs b/tests/ui/parser/bounds-type.rs index 47fec7b1e77f9..7cee6def32f83 100644 --- a/tests/ui/parser/bounds-type.rs +++ b/tests/ui/parser/bounds-type.rs @@ -1,4 +1,5 @@ //@ compile-flags: -Z parse-only +//@ edition: 2021 struct S< T: 'a + Tr, // OK @@ -10,10 +11,20 @@ struct S< T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds T: ~const Tr, // OK - T: ~const ?Tr, // OK + T: ~const ?Tr, //~ ERROR `~const` trait not allowed with `?` trait polarity modifier T: ~const Tr + 'a, // OK T: ~const 'a, //~ ERROR `~const` may only modify trait bounds, not lifetime bounds T: const 'a, //~ ERROR `const` may only modify trait bounds, not lifetime bounds + + T: async Tr, // OK + T: async ?Tr, //~ ERROR `async` trait not allowed with `?` trait polarity modifier + T: async Tr + 'a, // OK + T: async 'a, //~ ERROR `async` may only modify trait bounds, not lifetime bounds + + T: const async Tr, // OK + T: const async ?Tr, //~ ERROR `const async` trait not allowed with `?` trait polarity modifier + T: const async Tr + 'a, // OK + T: const async 'a, //~ ERROR `const` may only modify trait bounds, not lifetime bounds >; fn main() {} diff --git a/tests/ui/parser/bounds-type.stderr b/tests/ui/parser/bounds-type.stderr index fa8ab6812b156..09c35c12b000a 100644 --- a/tests/ui/parser/bounds-type.stderr +++ b/tests/ui/parser/bounds-type.stderr @@ -1,5 +1,5 @@ error: `for<...>` binder not allowed with `?` trait polarity modifier - --> $DIR/bounds-type.rs:8:16 + --> $DIR/bounds-type.rs:9:16 | LL | T: for<'a> ?Trait, | ---- ^ @@ -7,22 +7,58 @@ LL | T: for<'a> ?Trait, | there is not a well-defined meaning for a higher-ranked `?` trait error: `?` may only modify trait bounds, not lifetime bounds - --> $DIR/bounds-type.rs:10:8 + --> $DIR/bounds-type.rs:11:8 | LL | T: ?'a, | ^ +error: `~const` trait not allowed with `?` trait polarity modifier + --> $DIR/bounds-type.rs:14:15 + | +LL | T: ~const ?Tr, + | ------ ^ + | | + | there is not a well-defined meaning for a `~const ?` trait + error: `~const` may only modify trait bounds, not lifetime bounds - --> $DIR/bounds-type.rs:15:8 + --> $DIR/bounds-type.rs:16:8 | LL | T: ~const 'a, | ^^^^^^ error: `const` may only modify trait bounds, not lifetime bounds - --> $DIR/bounds-type.rs:16:8 + --> $DIR/bounds-type.rs:17:8 | LL | T: const 'a, | ^^^^^ -error: aborting due to 4 previous errors +error: `async` trait not allowed with `?` trait polarity modifier + --> $DIR/bounds-type.rs:20:14 + | +LL | T: async ?Tr, + | ----- ^ + | | + | there is not a well-defined meaning for a `async ?` trait + +error: `async` may only modify trait bounds, not lifetime bounds + --> $DIR/bounds-type.rs:22:8 + | +LL | T: async 'a, + | ^^^^^ + +error: `const async` trait not allowed with `?` trait polarity modifier + --> $DIR/bounds-type.rs:25:20 + | +LL | T: const async ?Tr, + | ----------- ^ + | | + | there is not a well-defined meaning for a `const async ?` trait + +error: `const` may only modify trait bounds, not lifetime bounds + --> $DIR/bounds-type.rs:27:8 + | +LL | T: const async 'a, + | ^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.rs index 37e285f2c6590..aaab8e819a39c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.rs @@ -1,17 +1,17 @@ #![feature(const_trait_impl)] const fn maybe_const_maybe() {} -//~^ ERROR `~const` and `?` are mutually exclusive +//~^ ERROR `~const` trait not allowed with `?` trait polarity modifier fn const_maybe() {} -//~^ ERROR `const` and `?` are mutually exclusive +//~^ ERROR `const` trait not allowed with `?` trait polarity modifier const fn maybe_const_negative() {} -//~^ ERROR `~const` and `!` are mutually exclusive +//~^ ERROR `~const` trait not allowed with `!` trait polarity modifier //~| ERROR negative bounds are not supported fn const_negative() {} -//~^ ERROR `const` and `!` are mutually exclusive +//~^ ERROR `const` trait not allowed with `!` trait polarity modifier //~| ERROR negative bounds are not supported #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.stderr index 1938f740170b5..18e4d160f5f46 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/mutually-exclusive-trait-bound-modifiers.stderr @@ -1,26 +1,34 @@ -error: `~const` and `?` are mutually exclusive - --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:3:31 +error: `~const` trait not allowed with `?` trait polarity modifier + --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:3:38 | LL | const fn maybe_const_maybe() {} - | ^^^^^^^^^^^^^ + | ------ ^ + | | + | there is not a well-defined meaning for a `~const ?` trait -error: `const` and `?` are mutually exclusive - --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:6:19 +error: `const` trait not allowed with `?` trait polarity modifier + --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:6:25 | LL | fn const_maybe() {} - | ^^^^^^^^^^^^ + | ----- ^ + | | + | there is not a well-defined meaning for a `const ?` trait -error: `~const` and `!` are mutually exclusive - --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:9:34 +error: `~const` trait not allowed with `!` trait polarity modifier + --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:9:41 | LL | const fn maybe_const_negative() {} - | ^^^^^^^^^^^^^ + | ------ ^ + | | + | there is not a well-defined meaning for a `~const !` trait -error: `const` and `!` are mutually exclusive - --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:13:22 +error: `const` trait not allowed with `!` trait polarity modifier + --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:13:28 | LL | fn const_negative() {} - | ^^^^^^^^^^^^ + | ----- ^ + | | + | there is not a well-defined meaning for a `const !` trait error: negative bounds are not supported --> $DIR/mutually-exclusive-trait-bound-modifiers.rs:9:41 From c1fd25d0aa56c27f1e8dd6fe00981eefba0d2e11 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 11 Jul 2024 15:44:03 +0200 Subject: [PATCH 073/806] Merge commit 'b794b8e08c16517a941dc598bb1483e8e12a8592' into clippy-subtree-update --- .github/workflows/lintcheck.yml | 4 +- CHANGELOG.md | 6 + book/src/lint_configuration.md | 3 +- clippy.toml | 4 + clippy_config/src/conf.rs | 3 +- clippy_config/src/msrvs.rs | 4 +- clippy_dev/src/new_lint.rs | 4 +- clippy_lints/src/almost_complete_range.rs | 104 +-- clippy_lints/src/assertions_on_constants.rs | 20 +- clippy_lints/src/assigning_clones.rs | 496 ++++++--------- clippy_lints/src/await_holding_invalid.rs | 46 +- clippy_lints/src/bool_to_int_with_if.rs | 134 ++-- clippy_lints/src/byte_char_slices.rs | 80 +++ clippy_lints/src/casts/mod.rs | 113 ++-- clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- clippy_lints/src/cfg_not_test.rs | 60 ++ clippy_lints/src/declared_lints.rs | 10 +- clippy_lints/src/default.rs | 10 +- clippy_lints/src/default_numeric_fallback.rs | 2 + clippy_lints/src/dereference.rs | 37 +- clippy_lints/src/disallowed_methods.rs | 42 +- clippy_lints/src/disallowed_names.rs | 46 +- clippy_lints/src/disallowed_script_idents.rs | 41 +- clippy_lints/src/doc/lazy_continuation.rs | 29 +- clippy_lints/src/doc/mod.rs | 21 +- clippy_lints/src/endian_bytes.rs | 4 +- .../src/functions/impl_trait_in_params.rs | 8 +- clippy_lints/src/incompatible_msrv.rs | 4 +- clippy_lints/src/inherent_to_string.rs | 5 +- clippy_lints/src/init_numbered_fields.rs | 87 +-- clippy_lints/src/inline_fn_without_body.rs | 41 +- clippy_lints/src/instant_subtraction.rs | 21 +- clippy_lints/src/items_after_statements.rs | 59 +- .../src/iter_not_returning_iterator.rs | 21 +- clippy_lints/src/iter_without_into_iter.rs | 4 +- clippy_lints/src/large_const_arrays.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 17 +- clippy_lints/src/large_futures.rs | 43 +- clippy_lints/src/large_include_file.rs | 20 +- clippy_lints/src/legacy_numeric_constants.rs | 46 +- clippy_lints/src/len_zero.rs | 19 +- clippy_lints/src/let_if_seq.rs | 8 +- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/let_with_type_underscore.rs | 17 +- clippy_lints/src/lib.rs | 24 +- clippy_lints/src/literal_representation.rs | 16 +- clippy_lints/src/manual_bits.rs | 5 +- clippy_lints/src/manual_float_methods.rs | 29 +- clippy_lints/src/manual_let_else.rs | 6 +- clippy_lints/src/manual_main_separator_str.rs | 8 +- clippy_lints/src/manual_non_exhaustive.rs | 14 +- clippy_lints/src/manual_range_patterns.rs | 5 +- clippy_lints/src/manual_rem_euclid.rs | 49 +- clippy_lints/src/manual_retain.rs | 3 +- clippy_lints/src/manual_rotate.rs | 117 ++++ .../src/manual_slice_size_calculation.rs | 6 +- clippy_lints/src/manual_strip.rs | 7 +- clippy_lints/src/manual_unwrap_or_default.rs | 9 +- clippy_lints/src/map_unit_fn.rs | 13 +- clippy_lints/src/matches/mod.rs | 13 +- .../matches/significant_drop_in_scrutinee.rs | 111 +++- clippy_lints/src/methods/manual_inspect.rs | 26 +- clippy_lints/src/methods/mod.rs | 9 +- .../src/methods/unnecessary_to_owned.rs | 70 ++- .../src/methods/unwrap_expect_used.rs | 4 +- clippy_lints/src/minmax.rs | 37 +- clippy_lints/src/misc.rs | 6 +- clippy_lints/src/missing_assert_message.rs | 4 +- clippy_lints/src/missing_const_for_fn.rs | 33 +- ...t.rs => missing_const_for_thread_local.rs} | 14 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 11 +- clippy_lints/src/no_effect.rs | 11 +- clippy_lints/src/operators/float_cmp.rs | 1 - clippy_lints/src/operators/mod.rs | 140 +++-- .../src/overflow_check_conditional.rs | 70 --- clippy_lints/src/panicking_overflow_checks.rs | 86 +++ clippy_lints/src/renamed_lints.rs | 2 + clippy_lints/src/returns.rs | 47 +- clippy_lints/src/same_name_method.rs | 2 +- clippy_lints/src/set_contains_or_insert.rs | 125 ++++ clippy_lints/src/utils/internal_lints.rs | 1 - .../internal_lints/compiler_lint_functions.rs | 73 --- clippy_lints/src/wildcard_imports.rs | 19 +- clippy_lints/src/write.rs | 5 +- clippy_utils/src/lib.rs | 67 +- clippy_utils/src/paths.rs | 1 + clippy_utils/src/ty.rs | 26 +- lintcheck/Cargo.toml | 1 + lintcheck/README.md | 16 +- lintcheck/lintcheck_crates.toml | 52 +- lintcheck/src/config.rs | 4 + lintcheck/src/driver.rs | 2 - lintcheck/src/input.rs | 288 +++++++++ lintcheck/src/json.rs | 99 ++- lintcheck/src/main.rs | 595 ++---------------- lintcheck/src/output.rs | 235 +++++++ lintcheck/src/popular_crates.rs | 2 +- lintcheck/src/recursive.rs | 7 +- rust-toolchain | 3 +- tests/ui-internal/disallow_span_lint.stderr | 16 +- .../await_holding_invalid_type.stderr | 6 +- .../needless_pass_by_ref_mut/clippy.toml | 1 + .../needless_pass_by_ref_mut.fixed | 10 + .../needless_pass_by_ref_mut.rs | 10 + .../needless_pass_by_ref_mut.stderr | 12 + .../conf_disallowed_methods.stderr | 36 +- tests/ui/assertions_on_constants.rs | 11 +- tests/ui/assertions_on_constants.stderr | 18 +- tests/ui/await_holding_lock.rs | 26 +- tests/ui/await_holding_lock.stderr | 78 +-- tests/ui/await_holding_refcell_ref.rs | 12 +- tests/ui/await_holding_refcell_ref.stderr | 24 +- tests/ui/byte_char_slices.fixed | 13 + tests/ui/byte_char_slices.rs | 13 + tests/ui/byte_char_slices.stderr | 38 ++ tests/ui/cfg_not_test.rs | 32 + tests/ui/cfg_not_test.stderr | 45 ++ tests/ui/crashes/ice-12616.stderr | 2 +- tests/ui/disallowed_names.rs | 1 + tests/ui/doc/doc_lazy_blank_line.fixed | 47 ++ tests/ui/doc/doc_lazy_blank_line.rs | 43 ++ tests/ui/doc/doc_lazy_blank_line.stderr | 56 ++ tests/ui/doc/doc_lazy_blockquote.fixed | 12 +- tests/ui/doc/doc_lazy_blockquote.rs | 12 +- tests/ui/doc/doc_lazy_blockquote.stderr | 12 +- tests/ui/doc/doc_lazy_list.fixed | 31 +- tests/ui/doc/doc_lazy_list.rs | 22 +- tests/ui/doc/doc_lazy_list.stderr | 49 +- tests/ui/doc/unbalanced_ticks.rs | 17 + tests/ui/doc/unbalanced_ticks.stderr | 18 +- tests/ui/explicit_auto_deref.fixed | 36 ++ tests/ui/explicit_auto_deref.rs | 36 ++ tests/ui/explicit_auto_deref.stderr | 8 +- tests/ui/float_cmp.rs | 6 - tests/ui/float_cmp.stderr | 21 +- tests/ui/float_cmp_const.rs | 8 - tests/ui/float_cmp_const.stderr | 29 +- ...ields.fixed => init_numbered_fields.fixed} | 9 + ...ered_fields.rs => init_numbered_fields.rs} | 9 + ...lds.stderr => init_numbered_fields.stderr} | 12 +- tests/ui/into_iter_without_iter.rs | 39 ++ tests/ui/iter_next_loop.rs | 2 +- tests/ui/iter_next_loop.stderr | 10 +- tests/ui/manual_inspect.stderr | 24 +- tests/ui/manual_rotate.fixed | 31 + tests/ui/manual_rotate.rs | 31 + tests/ui/manual_rotate.stderr | 71 +++ .../ui/missing_const_for_fn/cant_be_const.rs | 18 + .../missing_const_for_fn/could_be_const.fixed | 30 + .../ui/missing_const_for_fn/could_be_const.rs | 30 + .../could_be_const.stderr | 41 +- .../could_be_const_with_const_extern_fn.fixed | 14 + .../could_be_const_with_const_extern_fn.rs | 14 + ...could_be_const_with_const_extern_fn.stderr | 59 ++ ...d => missing_const_for_thread_local.fixed} | 2 +- ...t.rs => missing_const_for_thread_local.rs} | 2 +- ... => missing_const_for_thread_local.stderr} | 16 +- tests/ui/needless_pass_by_ref_mut.rs | 18 +- tests/ui/needless_pass_by_ref_mut.stderr | 52 +- tests/ui/needless_pass_by_ref_mut2.fixed | 4 +- tests/ui/needless_pass_by_ref_mut2.rs | 4 +- tests/ui/needless_pass_by_ref_mut2.stderr | 15 +- tests/ui/needless_return.fixed | 35 +- tests/ui/needless_return.rs | 35 +- tests/ui/needless_return.stderr | 28 +- tests/ui/overflow_check_conditional.rs | 36 -- tests/ui/overflow_check_conditional.stderr | 53 -- tests/ui/panicking_overflow_checks.rs | 27 + tests/ui/panicking_overflow_checks.stderr | 41 ++ tests/ui/ptr_as_ptr.stderr | 66 +- tests/ui/rename.fixed | 4 + tests/ui/rename.rs | 4 + tests/ui/rename.stderr | 130 ++-- tests/ui/set_contains_or_insert.rs | 83 +++ tests/ui/set_contains_or_insert.stderr | 61 ++ tests/ui/significant_drop_in_scrutinee.rs | 26 + tests/ui/significant_drop_in_scrutinee.stderr | 29 +- tests/ui/unnecessary_operation.fixed | 14 +- tests/ui/unnecessary_operation.rs | 14 +- tests/ui/unnecessary_operation.stderr | 8 +- tests/ui/unnecessary_to_owned.fixed | 19 + tests/ui/unnecessary_to_owned.rs | 19 + tests/ui/unnecessary_to_owned.stderr | 60 +- tests/ui/wildcard_imports.fixed | 3 + tests/ui/wildcard_imports.rs | 3 + tests/ui/wildcard_imports.stderr | 10 +- .../wildcard_imports_2021.edition2018.fixed | 3 + .../wildcard_imports_2021.edition2018.stderr | 10 +- .../wildcard_imports_2021.edition2021.fixed | 3 + .../wildcard_imports_2021.edition2021.stderr | 10 +- tests/ui/wildcard_imports_2021.rs | 3 + 191 files changed, 4147 insertions(+), 2494 deletions(-) create mode 100644 clippy_lints/src/byte_char_slices.rs create mode 100644 clippy_lints/src/cfg_not_test.rs create mode 100644 clippy_lints/src/manual_rotate.rs rename clippy_lints/src/{thread_local_initializer_can_be_made_const.rs => missing_const_for_thread_local.rs} (92%) delete mode 100644 clippy_lints/src/overflow_check_conditional.rs create mode 100644 clippy_lints/src/panicking_overflow_checks.rs create mode 100644 clippy_lints/src/set_contains_or_insert.rs delete mode 100644 clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs create mode 100644 lintcheck/src/input.rs create mode 100644 lintcheck/src/output.rs create mode 100644 tests/ui-toml/needless_pass_by_ref_mut/clippy.toml create mode 100644 tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed create mode 100644 tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs create mode 100644 tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr create mode 100644 tests/ui/byte_char_slices.fixed create mode 100644 tests/ui/byte_char_slices.rs create mode 100644 tests/ui/byte_char_slices.stderr create mode 100644 tests/ui/cfg_not_test.rs create mode 100644 tests/ui/cfg_not_test.stderr create mode 100644 tests/ui/doc/doc_lazy_blank_line.fixed create mode 100644 tests/ui/doc/doc_lazy_blank_line.rs create mode 100644 tests/ui/doc/doc_lazy_blank_line.stderr rename tests/ui/{numbered_fields.fixed => init_numbered_fields.fixed} (82%) rename tests/ui/{numbered_fields.rs => init_numbered_fields.rs} (84%) rename tests/ui/{numbered_fields.stderr => init_numbered_fields.stderr} (63%) create mode 100644 tests/ui/manual_rotate.fixed create mode 100644 tests/ui/manual_rotate.rs create mode 100644 tests/ui/manual_rotate.stderr create mode 100644 tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed create mode 100644 tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs create mode 100644 tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr rename tests/ui/{thread_local_initializer_can_be_made_const.fixed => missing_const_for_thread_local.fixed} (97%) rename tests/ui/{thread_local_initializer_can_be_made_const.rs => missing_const_for_thread_local.rs} (97%) rename tests/ui/{thread_local_initializer_can_be_made_const.stderr => missing_const_for_thread_local.stderr} (72%) delete mode 100644 tests/ui/overflow_check_conditional.rs delete mode 100644 tests/ui/overflow_check_conditional.stderr create mode 100644 tests/ui/panicking_overflow_checks.rs create mode 100644 tests/ui/panicking_overflow_checks.stderr create mode 100644 tests/ui/set_contains_or_insert.rs create mode 100644 tests/ui/set_contains_or_insert.stderr diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 91c98b3a2560c..f016a77005920 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -58,7 +58,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json + run: ./target/debug/lintcheck --format json --warn-all - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -86,7 +86,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json + run: ./target/debug/lintcheck --format json --warn-all - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ef2c7936406..55281f3cbec0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5236,6 +5236,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata @@ -5253,6 +5254,7 @@ Released 2018-09-13 [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts +[`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp @@ -5539,6 +5541,7 @@ Released 2018-09-13 [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain +[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once @@ -5587,6 +5590,7 @@ Released 2018-09-13 [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message [`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn +[`missing_const_for_thread_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_thread_local [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc @@ -5701,6 +5705,7 @@ Released 2018-09-13 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params +[`panicking_overflow_checks`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_overflow_checks [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl @@ -5797,6 +5802,7 @@ Released 2018-09-13 [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block [`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse +[`set_contains_or_insert`]: https://rust-lang.github.io/rust-clippy/master/index.html#set_contains_or_insert [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index cfd34c7d2a7d1..ad29339a84ad6 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -348,6 +348,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat * [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) * [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) +* [`needless_pass_by_ref_mut`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut) * [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) * [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) * [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) @@ -454,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy.toml b/clippy.toml index 62ed55beb1f34..319b72e8c5d50 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,5 +1,9 @@ avoid-breaking-exported-api = false +[[disallowed-methods]] +path = "rustc_lint::context::LintContext::lint" +reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" + [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index dbab3b106a80f..7f53aad679330 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -31,6 +31,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", + "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", @@ -262,7 +263,7 @@ define_Conf! { /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] /// ``` (arithmetic_side_effects_allowed_unary: FxHashSet = <_>::default()), - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN. + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index a5761d3270cd2..fc56ac5179686 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -25,8 +25,8 @@ msrv_aliases! { 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,63,0 { CLONE_INTO } - 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } - 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } + 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_FN } + 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 2e56eb8ec15f8..d762e30ef02e1 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -273,7 +273,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( r#" - use clippy_utils::msrvs::{{self, Msrv}}; + use clippy_config::msrvs::{{self, Msrv}}; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_session::impl_lint_pass; @@ -399,7 +399,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let _: fmt::Result = writedoc!( lint_file_contents, r#" - use clippy_utils::msrvs::{{self, Msrv}}; + use clippy_config::msrvs::{{self, Msrv}}; use rustc_lint::{{{context_import}, LintContext}}; use super::{name_upper}; diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 57a5cd8fba818..96e9c949b7500 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -6,7 +6,6 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -41,61 +40,80 @@ impl AlmostCompleteRange { } impl EarlyLintPass for AlmostCompleteRange { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { - let ctxt = e.span.ctxt(); - let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) - && let Some(end) = walk_span_to_context(end.span, ctxt) - && self.msrv.meets(msrvs::RANGE_INCLUSIVE) - { - Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) - } else { - None - }; - check_range(cx, e.span, start, end, sugg); + if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind + && is_incomplete_range(start, end) + && !in_external_macro(cx.sess(), e.span) + { + span_lint_and_then( + cx, + ALMOST_COMPLETE_RANGE, + e.span, + "almost complete ascii range", + |diag| { + let ctxt = e.span.ctxt(); + if let Some(start) = walk_span_to_context(start.span, ctxt) + && let Some(end) = walk_span_to_context(end.span, ctxt) + && self.msrv.meets(msrvs::RANGE_INCLUSIVE) + { + diag.span_suggestion( + trim_span(cx.sess().source_map(), start.between(end)), + "use an inclusive range", + "..=".to_owned(), + Applicability::MaybeIncorrect, + ); + } + }, + ); } } fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) { if let PatKind::Range(Some(start), Some(end), kind) = &p.kind && matches!(kind.node, RangeEnd::Excluded) + && is_incomplete_range(start, end) + && !in_external_macro(cx.sess(), p.span) { - let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { - "..=" - } else { - "..." - }; - check_range(cx, p.span, start, end, Some((kind.span, sugg))); + span_lint_and_then( + cx, + ALMOST_COMPLETE_RANGE, + p.span, + "almost complete ascii range", + |diag| { + diag.span_suggestion( + kind.span, + "use an inclusive range", + if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { + "..=".to_owned() + } else { + "...".to_owned() + }, + Applicability::MaybeIncorrect, + ); + }, + ); } } extract_msrv_attr!(EarlyContext); } -fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { - if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind - && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind - && matches!( - ( - LitKind::from_token_lit(start_token_lit), - LitKind::from_token_lit(end_token_lit), - ), - ( - Ok(LitKind::Byte(b'a') | LitKind::Char('a')), - Ok(LitKind::Byte(b'z') | LitKind::Char('z')) - ) | ( - Ok(LitKind::Byte(b'A') | LitKind::Char('A')), - Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), - ) | ( - Ok(LitKind::Byte(b'0') | LitKind::Char('0')), - Ok(LitKind::Byte(b'9') | LitKind::Char('9')), +fn is_incomplete_range(start: &Expr, end: &Expr) -> bool { + match (&start.peel_parens().kind, &end.peel_parens().kind) { + (&ExprKind::Lit(start_lit), &ExprKind::Lit(end_lit)) => { + matches!( + (LitKind::from_token_lit(start_lit), LitKind::from_token_lit(end_lit),), + ( + Ok(LitKind::Byte(b'a') | LitKind::Char('a')), + Ok(LitKind::Byte(b'z') | LitKind::Char('z')) + ) | ( + Ok(LitKind::Byte(b'A') | LitKind::Char('A')), + Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), + ) | ( + Ok(LitKind::Byte(b'0') | LitKind::Char('0')), + Ok(LitKind::Byte(b'9') | LitKind::Char('9')), + ) ) - ) - && !in_external_macro(cx.sess(), span) - { - span_lint_and_then(cx, ALMOST_COMPLETE_RANGE, span, "almost complete ascii range", |diag| { - if let Some((span, sugg)) = sugg { - diag.span_suggestion(span, "use an inclusive range", sugg, Applicability::MaybeIncorrect); - } - }); + }, + _ => false, } } diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 2003dd1fb0e2c..ed4cdce8cb886 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,7 +1,8 @@ -use clippy_utils::consts::{constant_with_source, Constant, ConstantSource}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_inside_always_const_context; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; -use rustc_hir::{Expr, Item, ItemKind, Node}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -42,17 +43,16 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return; }; - let Some((Constant::Bool(val), source)) = constant_with_source(cx, cx.typeck_results(), condition) else { + let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return; }; - if let ConstantSource::Constant = source - && let Node::Item(Item { - kind: ItemKind::Const(..), - .. - }) = cx.tcx.parent_hir_node(e.hir_id) - { - return; + + match condition.kind { + ExprKind::Path(..) | ExprKind::Lit(_) => {}, + _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return, + _ => {}, } + if val { span_lint_and_help( cx, diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 406f38f411ecd..0de0031ed24ff 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -1,18 +1,16 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::HirNode; use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_trait_method, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::{ExpnKind, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -68,165 +66,82 @@ impl AssigningClones { impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { - fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) { - // Do not fire the lint in macros - let ctxt = assign_expr.span().ctxt(); - let expn_data = ctxt.outer_expn_data(); - match expn_data.kind { - ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, - ExpnKind::Root => {}, - } - - let ExprKind::Assign(lhs, rhs, _span) = assign_expr.kind else { - return; - }; - - let Some(call) = extract_call(cx, rhs) else { - return; - }; - - if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { - suggest(cx, ctxt, assign_expr, lhs, &call); - } - } - - extract_msrv_attr!(LateContext); -} - -// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`. -fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { - let fn_def_id = clippy_utils::fn_def_id(cx, expr)?; - - // Fast paths to only check method calls without arguments or function calls with a single argument - let (target, kind, resolved_method) = match expr.kind { - ExprKind::MethodCall(path, receiver, [], _span) => { - let args = cx.typeck_results().node_args(expr.hir_id); - - // If we could not resolve the method, don't apply the lint - let Ok(Some(resolved_method)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_def_id, args) else { - return None; - }; - if is_trait_method(cx, expr, sym::Clone) && path.ident.name == sym::clone { - (TargetTrait::Clone, CallKind::MethodCall { receiver }, resolved_method) - } else if is_trait_method(cx, expr, sym::ToOwned) && path.ident.name.as_str() == "to_owned" { - (TargetTrait::ToOwned, CallKind::MethodCall { receiver }, resolved_method) - } else { - return None; + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = e.kind + && let typeck = cx.typeck_results() + && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind { + ExprKind::Call(f, [arg]) + if let ExprKind::Path(fn_path) = &f.kind + && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() => + { + (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id)) + }, + ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => { + (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id)) + }, + _ => return, } - }, - ExprKind::Call(function, [arg]) => { - let kind = cx.typeck_results().node_type(function.hir_id).kind(); - - // If we could not resolve the method, don't apply the lint - let Ok(Some(resolved_method)) = (match kind { - ty::FnDef(_, args) => Instance::try_resolve(cx.tcx, cx.param_env, fn_def_id, args), - _ => Ok(None), - }) else { - return None; - }; - if cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) { - ( - TargetTrait::ToOwned, - CallKind::FunctionCall { self_arg: arg }, - resolved_method, - ) - } else if let Some(trait_did) = cx.tcx.trait_of_item(fn_def_id) - && cx.tcx.is_diagnostic_item(sym::Clone, trait_did) - { - ( - TargetTrait::Clone, - CallKind::FunctionCall { self_arg: arg }, - resolved_method, - ) - } else { - return None; + && let ctxt = e.span.ctxt() + // Don't lint in macros. + && ctxt.is_root() + && let which_trait = match fn_name { + sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, + _ if fn_name.as_str() == "to_owned" + && is_diag_trait_item(cx, fn_id, sym::ToOwned) + && self.msrv.meets(msrvs::CLONE_INTO) => + { + CloneTrait::ToOwned + }, + _ => return, } - }, - _ => return None, - }; - - Some(CallCandidate { - span: expr.span, - target, - kind, - method_def_id: resolved_method.def_id(), - }) -} - -// Return true if we find that the called method has a custom implementation and isn't derived or -// provided by default by the corresponding trait. -fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool { - // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63. - // If the current MSRV is below that, don't suggest the lint. - if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) { - return false; - } - - // If the left-hand side is a local variable, it might be uninitialized at this point. - // In that case we do not want to suggest the lint. - if let Some(local) = path_to_local(lhs) { - // TODO: This check currently bails if the local variable has no initializer. - // That is overly conservative - the lint should fire even if there was no initializer, - // but the variable has been initialized before `lhs` was evaluated. - if !local_is_initialized(cx, local) { - return false; - } - } - - let Some(impl_block) = cx.tcx.impl_of_method(call.method_def_id) else { - return false; - }; - - // If the method implementation comes from #[derive(Clone)], then don't suggest the lint. - // Automatically generated Clone impls do not currently override `clone_from`. - // See e.g. https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 for more details. - if cx.tcx.is_builtin_derived(impl_block) { - return false; - } - - // If the call expression is inside an impl block that contains the method invoked by the - // call expression, we bail out to avoid suggesting something that could result in endless - // recursion. - if let Some(local_block_id) = impl_block.as_local() - && let Some(block) = cx.tcx.hir_node_by_def_id(local_block_id).as_owner() - { - let impl_block_owner = block.def_id(); - if cx - .tcx - .hir() - .parent_id_iter(lhs.hir_id) - .any(|parent| parent.owner == impl_block_owner) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_id, fn_gen_args) + // TODO: This check currently bails if the local variable has no initializer. + // That is overly conservative - the lint should fire even if there was no initializer, + // but the variable has been initialized before `lhs` was evaluated. + && path_to_local(lhs).map_or(true, |lhs| local_is_initialized(cx, lhs)) + && let Some(resolved_impl) = cx.tcx.impl_of_method(resolved_fn.def_id()) + // Derived forms don't implement `clone_from`/`clone_into`. + // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 + && !cx.tcx.is_builtin_derived(resolved_impl) + // Don't suggest calling a function we're implementing. + && resolved_impl.as_local().map_or(true, |block_id| { + cx.tcx.hir().parent_owner_iter(e.hir_id).all(|(id, _)| id.def_id != block_id) + }) + && let resolved_assoc_items = cx.tcx.associated_items(resolved_impl) + // Only suggest if `clone_from`/`clone_into` is explicitly implemented + && resolved_assoc_items.in_definition_order().any(|assoc| + match which_trait { + CloneTrait::Clone => assoc.name == sym::clone_from, + CloneTrait::ToOwned => assoc.name.as_str() == "clone_into", + } + ) + && !clone_source_borrows_from_dest(cx, lhs, rhs.span) { - return false; + span_lint_and_then( + cx, + ASSIGNING_CLONES, + e.span, + match which_trait { + CloneTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient", + CloneTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient", + }, + |diag| { + let mut app = Applicability::Unspecified; + diag.span_suggestion( + e.span, + match which_trait { + CloneTrait::Clone => "use `clone_from()`", + CloneTrait::ToOwned => "use `clone_into()`", + }, + build_sugg(cx, ctxt, lhs, fn_arg, which_trait, call_kind, &mut app), + app, + ); + }, + ); } } - // Find the function for which we want to check that it is implemented. - let provided_fn = match call.target { - TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| { - cx.tcx - .provided_trait_methods(clone) - .find(|item| item.name == sym::clone_from) - }), - TargetTrait::ToOwned => cx.tcx.get_diagnostic_item(sym::ToOwned).and_then(|to_owned| { - cx.tcx - .provided_trait_methods(to_owned) - .find(|item| item.name.as_str() == "clone_into") - }), - }; - let Some(provided_fn) = provided_fn else { - return false; - }; - - if clone_source_borrows_from_dest(cx, lhs, call.span) { - return false; - } - - // Now take a look if the impl block defines an implementation for the method that we're interested - // in. If not, then we're using a default implementation, which is not interesting, so we will - // not suggest the lint. - let implemented_fns = cx.tcx.impl_item_implementor_ids(impl_block); - implemented_fns.contains_key(&provided_fn.def_id) + extract_msrv_attr!(LateContext); } /// Checks if the data being cloned borrows from the place that is being assigned to: @@ -239,16 +154,6 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC /// /// This cannot be written `s2.clone_into(&mut s)` because it has conflicting borrows. fn clone_source_borrows_from_dest(cx: &LateContext<'_>, lhs: &Expr<'_>, call_span: Span) -> bool { - /// If this basic block only exists to drop a local as part of an assignment, returns its - /// successor. Otherwise returns the basic block that was passed in. - fn skip_drop_block(mir: &mir::Body<'_>, bb: mir::BasicBlock) -> mir::BasicBlock { - if let mir::TerminatorKind::Drop { target, .. } = mir.basic_blocks[bb].terminator().kind { - target - } else { - bb - } - } - let Some(mir) = enclosing_mir(cx.tcx, lhs.hir_id) else { return false; }; @@ -267,172 +172,123 @@ fn clone_source_borrows_from_dest(cx: &LateContext<'_>, lhs: &Expr<'_>, call_spa // // bb2: // s = s_temp - for bb in mir.basic_blocks.iter() { - let terminator = bb.terminator(); - - // Look for the to_owned/clone call. - if terminator.source_info.span != call_span { - continue; + if let Some(terminator) = mir.basic_blocks.iter() + .map(mir::BasicBlockData::terminator) + .find(|term| term.source_info.span == call_span) + && let mir::TerminatorKind::Call { ref args, target: Some(assign_bb), .. } = terminator.kind + && let [source] = &**args + && let mir::Operand::Move(source) = &source.node + && let assign_bb = &mir.basic_blocks[assign_bb] + && let assign_bb = match assign_bb.terminator().kind { + // Skip the drop of the assignment's destination. + mir::TerminatorKind::Drop { target, .. } => &mir.basic_blocks[target], + _ => assign_bb, } - - if let mir::TerminatorKind::Call { ref args, target: Some(assign_bb), .. } = terminator.kind - && let [source] = &**args - && let mir::Operand::Move(source) = &source.node - && let assign_bb = skip_drop_block(mir, assign_bb) - // Skip any storage statements as they are just noise - && let Some(assignment) = mir.basic_blocks[assign_bb].statements - .iter() - .find(|stmt| { - !matches!(stmt.kind, mir::StatementKind::StorageDead(_) | mir::StatementKind::StorageLive(_)) - }) - && let mir::StatementKind::Assign(box (borrowed, _)) = &assignment.kind - && let Some(borrowers) = borrow_map.get(&borrowed.local) - && borrowers.contains(source.local) - { - return true; - } - - return false; + // Skip any storage statements as they are just noise + && let Some(assignment) = assign_bb.statements + .iter() + .find(|stmt| { + !matches!(stmt.kind, mir::StatementKind::StorageDead(_) | mir::StatementKind::StorageLive(_)) + }) + && let mir::StatementKind::Assign(box (borrowed, _)) = &assignment.kind + && let Some(borrowers) = borrow_map.get(&borrowed.local) + { + borrowers.contains(source.local) + } else { + false } - false -} - -fn suggest<'tcx>( - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - assign_expr: &Expr<'tcx>, - lhs: &Expr<'tcx>, - call: &CallCandidate<'tcx>, -) { - span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { - let mut applicability = Applicability::Unspecified; - - diag.span_suggestion( - assign_expr.span, - call.suggestion_msg(), - call.suggested_replacement(cx, ctxt, lhs, &mut applicability), - applicability, - ); - }); } -#[derive(Copy, Clone, Debug)] -enum CallKind<'tcx> { - MethodCall { receiver: &'tcx Expr<'tcx> }, - FunctionCall { self_arg: &'tcx Expr<'tcx> }, -} - -#[derive(Copy, Clone, Debug)] -enum TargetTrait { +#[derive(Clone, Copy)] +enum CloneTrait { Clone, ToOwned, } -#[derive(Debug)] -struct CallCandidate<'tcx> { - span: Span, - target: TargetTrait, - kind: CallKind<'tcx>, - // DefId of the called method from an impl block that implements the target trait - method_def_id: DefId, +#[derive(Copy, Clone)] +enum CallKind { + Ufcs, + Method, } -impl<'tcx> CallCandidate<'tcx> { - #[inline] - fn message(&self) -> &'static str { - match self.target { - TargetTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient", - TargetTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient", - } - } - - #[inline] - fn suggestion_msg(&self) -> &'static str { - match self.target { - TargetTrait::Clone => "use `clone_from()`", - TargetTrait::ToOwned => "use `clone_into()`", - } - } - - fn suggested_replacement( - &self, - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - lhs: &Expr<'tcx>, - applicability: &mut Applicability, - ) -> String { - match self.target { - TargetTrait::Clone => { - match self.kind { - CallKind::MethodCall { receiver } => { - let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) - } else { - // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` - Sugg::hir_with_applicability(cx, lhs, "_", applicability) - } - .maybe_par(); - - // Determine whether we need to reference the argument to clone_from(). - let clone_receiver_type = cx.typeck_results().expr_ty(receiver); - let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); - let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); - if clone_receiver_type != clone_receiver_adj_type { - // The receiver may have been a value type, so we need to add an `&` to - // be sure the argument to clone_from will be a reference. - arg_sugg = arg_sugg.addr(); - }; - - format!("{receiver_sugg}.clone_from({arg_sugg})") - }, - CallKind::FunctionCall { self_arg, .. } => { - let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", applicability) - } else { - // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` - Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() - }; - // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. - let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); - - format!("Clone::clone_from({self_sugg}, {rhs_sugg})") - }, - } - }, - TargetTrait::ToOwned => { - let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` - // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` - let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", applicability).maybe_par(); - let inner_type = cx.typeck_results().expr_ty(ref_expr); - // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it - // deref to a mutable reference. - if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) { - sugg +fn build_sugg<'tcx>( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + lhs: &'tcx Expr<'_>, + fn_arg: &'tcx Expr<'_>, + which_trait: CloneTrait, + call_kind: CallKind, + app: &mut Applicability, +) -> String { + match which_trait { + CloneTrait::Clone => { + match call_kind { + CallKind::Method => { + let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) } else { - sugg.mut_addr() + // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", app) } + .maybe_par(); + + // Determine whether we need to reference the argument to clone_from(). + let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg); + let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(fn_arg); + let mut arg_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app); + if clone_receiver_type != clone_receiver_adj_type { + // The receiver may have been a value type, so we need to add an `&` to + // be sure the argument to clone_from will be a reference. + arg_sugg = arg_sugg.addr(); + }; + + format!("{receiver_sugg}.clone_from({arg_sugg})") + }, + CallKind::Ufcs => { + let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr() + }; + // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. + let rhs_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app); + + format!("Clone::clone_from({self_sugg}, {rhs_sugg})") + }, + } + }, + CloneTrait::ToOwned => { + let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { + // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` + // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` + let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par(); + let inner_type = cx.typeck_results().expr_ty(ref_expr); + // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it + // deref to a mutable reference. + if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) { + sugg } else { - // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` - // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` - Sugg::hir_with_applicability(cx, lhs, "_", applicability) - .maybe_par() - .mut_addr() - }; - - match self.kind { - CallKind::MethodCall { receiver } => { - let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); - format!("{receiver_sugg}.clone_into({rhs_sugg})") - }, - CallKind::FunctionCall { self_arg, .. } => { - let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); - format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") - }, + sugg.mut_addr() } - }, - } + } else { + // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` + // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` + Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr() + }; + + match call_kind { + CallKind::Method => { + let receiver_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app); + format!("{receiver_sugg}.clone_into({rhs_sugg})") + }, + CallKind::Ufcs => { + let self_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app); + format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") + }, + } + }, } } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index f25a474d9bbd6..d4a1e2780d083 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -11,21 +11,25 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Checks for calls to await while holding a non-async-aware MutexGuard. + /// Checks for calls to `await` while holding a non-async-aware + /// `MutexGuard`. /// /// ### Why is this bad? - /// The Mutex types found in std::sync and parking_lot - /// are not designed to operate in an async context across await points. + /// The Mutex types found in [`std::sync`][https://doc.rust-lang.org/stable/std/sync/] and + /// [`parking_lot`](https://docs.rs/parking_lot/latest/parking_lot/) are + /// not designed to operate in an async context across await points. /// - /// There are two potential solutions. One is to use an async-aware Mutex - /// type. Many asynchronous foundation crates provide such a Mutex type. The - /// other solution is to ensure the mutex is unlocked before calling await, - /// either by introducing a scope or an explicit call to Drop::drop. + /// There are two potential solutions. One is to use an async-aware `Mutex` + /// type. Many asynchronous foundation crates provide such a `Mutex` type. + /// The other solution is to ensure the mutex is unlocked before calling + /// `await`, either by introducing a scope or an explicit call to + /// [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html). /// /// ### Known problems /// Will report false positive for explicitly dropped guards - /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is - /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard. + /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A + /// workaround for this is to wrap the `.lock()` call in a block instead of + /// explicitly dropping the guard. /// /// ### Example /// ```no_run @@ -73,11 +77,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`. + /// Checks for calls to `await` while holding a `RefCell`, `Ref`, or `RefMut`. /// /// ### Why is this bad? /// `RefCell` refs only check for exclusive mutable access - /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// at runtime. Holding a `RefCell` ref across an await suspension point /// risks panics from a mutable ref shared while other refs are outstanding. /// /// ### Known problems @@ -131,13 +135,13 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Allows users to configure types which should not be held across `await` + /// Allows users to configure types which should not be held across await /// suspension points. /// /// ### Why is this bad? - /// There are some types which are perfectly "safe" to be used concurrently - /// from a memory access perspective but will cause bugs at runtime if they - /// are held in such a way. + /// There are some types which are perfectly safe to use concurrently from + /// a memory access perspective, but that will cause bugs at runtime if + /// they are held in such a way. /// /// ### Example /// @@ -228,15 +232,15 @@ impl AwaitHolding { cx, AWAIT_HOLDING_LOCK, ty_cause.source_info.span, - "this `MutexGuard` is held across an `await` point", + "this `MutexGuard` is held across an await point", |diag| { diag.help( "consider using an async-aware `Mutex` type or ensuring the \ - `MutexGuard` is dropped before calling await", + `MutexGuard` is dropped before calling `await`", ); diag.span_note( await_points(), - "these are all the `await` points this lock is held through", + "these are all the await points this lock is held through", ); }, ); @@ -245,12 +249,12 @@ impl AwaitHolding { cx, AWAIT_HOLDING_REFCELL_REF, ty_cause.source_info.span, - "this `RefCell` reference is held across an `await` point", + "this `RefCell` reference is held across an await point", |diag| { diag.help("ensure the reference is dropped before calling `await`"); diag.span_note( await_points(), - "these are all the `await` points this reference is held through", + "these are all the await points this reference is held through", ); }, ); @@ -268,7 +272,7 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPa AWAIT_HOLDING_INVALID_TYPE, span, format!( - "`{}` may not be held across an `await` point per `clippy.toml`", + "`{}` may not be held across an await point per `clippy.toml`", disallowed.path() ), |diag| { diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index cfb76cab6dc23..561ca9bd9866d 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,13 +1,11 @@ -use clippy_utils::higher::If; -use rustc_ast::LitKind; -use rustc_hir::{Block, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; - use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{in_constant, is_else_clause, is_integer_literal}; +use clippy_utils::{in_constant, is_else_clause}; +use rustc_ast::LitKind; use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does @@ -47,80 +45,64 @@ declare_clippy_lint! { declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { - check_if_else(cx, expr); - } - } -} - -fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let Some(If { - cond, - then, - r#else: Some(r#else), - }) = If::hir(expr) - && let Some(then_lit) = int_literal(then) - && let Some(else_lit) = int_literal(r#else) - { - let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { - false - } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) { - true - } else { - // Expression isn't boolean, exit - return; - }; - let mut applicability = Applicability::MachineApplicable; - let snippet = { - let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); - if inverted { - sugg = !sugg; - } - sugg - }; - - let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::If(cond, then, Some(else_)) = expr.kind + && matches!(cond.kind, ExprKind::DropTemps(_)) + && let Some(then_lit) = as_int_bool_lit(then) + && let Some(else_lit) = as_int_bool_lit(else_) + && then_lit != else_lit + && !expr.span.from_expansion() + && !in_constant(cx, expr.hir_id) + { + let ty = cx.typeck_results().expr_ty(then); + let mut applicability = Applicability::MachineApplicable; + let snippet = { + let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); + if !then_lit { + sugg = !sugg; + } + sugg + }; + let suggestion = { + let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); + // when used in else clause if statement should be wrapped in curly braces + if is_else_clause(cx.tcx, expr) { + s = s.blockify(); + } + s + }; - let suggestion = { - let wrap_in_curly = is_else_clause(cx.tcx, expr); - let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); - if wrap_in_curly { - s = s.blockify(); - } - s - }; // when used in else clause if statement should be wrapped in curly braces + let into_snippet = snippet.clone().maybe_par(); + let as_snippet = snippet.as_ty(ty); - let into_snippet = snippet.clone().maybe_par(); - let as_snippet = snippet.as_ty(ty); - - span_lint_and_then( - cx, - BOOL_TO_INT_WITH_IF, - expr.span, - "boolean to int conversion using if", - |diag| { - diag.span_suggestion(expr.span, "replace with from", suggestion, applicability); - diag.note(format!( - "`{as_snippet}` or `{into_snippet}.into()` can also be valid options" - )); - }, - ); - }; + span_lint_and_then( + cx, + BOOL_TO_INT_WITH_IF, + expr.span, + "boolean to int conversion using if", + |diag| { + diag.span_suggestion(expr.span, "replace with from", suggestion, applicability); + diag.note(format!( + "`{as_snippet}` or `{into_snippet}.into()` can also be valid options" + )); + }, + ); + } + } } -// If block contains only a int literal expression, return literal expression -fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> { - if let ExprKind::Block(block, _) = expr.kind - && let Block { - stmts: [], // Shouldn't lint if statements with side effects - expr: Some(expr), - .. - } = block - && let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Int(_, _) = lit.node +fn as_int_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Block(b, _) = e.kind + && b.stmts.is_empty() + && let Some(e) = b.expr + && let ExprKind::Lit(lit) = e.kind + && let LitKind::Int(x, _) = lit.node { - Some(expr) + match x.get() { + 0 => Some(false), + 1 => Some(true), + _ => None, + } } else { None } diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs new file mode 100644 index 0000000000000..a9fe190f1777e --- /dev/null +++ b/clippy_lints/src/byte_char_slices.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_ast::token::{Lit, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for hard to read slices of byte characters, that could be more easily expressed as a + /// byte string. + /// + /// ### Why is this bad? + /// + /// Potentially makes the string harder to read. + /// + /// ### Example + /// ```ignore + /// &[b'H', b'e', b'l', b'l', b'o']; + /// ``` + /// Use instead: + /// ```ignore + /// b"Hello" + /// ``` + #[clippy::version = "1.68.0"] + pub BYTE_CHAR_SLICES, + style, + "hard to read byte char slice" +} +declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]); + +impl EarlyLintPass for ByteCharSlice { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let Some(slice) = is_byte_char_slices(expr) + && !expr.span.from_expansion() + { + span_lint_and_sugg( + cx, + BYTE_CHAR_SLICES, + expr.span, + "can be more succinctly written as a byte str", + "try", + format!("b\"{slice}\""), + Applicability::MaybeIncorrect, + ); + } + } +} + +fn is_byte_char_slices(expr: &Expr) -> Option { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind { + match &expr.kind { + ExprKind::Array(members) => { + if members.is_empty() { + return None; + } + + members + .iter() + .map(|member| match &member.kind { + ExprKind::Lit(Lit { + kind: LitKind::Byte, + symbol, + .. + }) => Some(symbol.as_str()), + _ => None, + }) + .map(|maybe_quote| match maybe_quote { + Some("\"") => Some("\\\""), + Some("\\'") => Some("'"), + other => other, + }) + .collect::>() + }, + _ => None, + } + } else { + None + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index e60c36ced75da..54f0c7c468719 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -32,7 +32,7 @@ use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for casts from any numerical to a float type where + /// Checks for casts from any numeric type to a float type where /// the receiving type cannot store all values from the original type without /// rounding errors. This possible rounding is to be expected, so this lint is /// `Allow` by default. @@ -58,14 +58,14 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts from a signed to an unsigned numerical + /// Checks for casts from a signed to an unsigned numeric /// type. In this case, negative values wrap around to large positive values, - /// which can be quite surprising in practice. However, as the cast works as + /// which can be quite surprising in practice. However, since the cast works as /// defined, this lint is `Allow` by default. /// /// ### Why is this bad? /// Possibly surprising results. You can activate this lint - /// as a one-time check to see where numerical wrapping can arise. + /// as a one-time check to see where numeric wrapping can arise. /// /// ### Example /// ```no_run @@ -80,7 +80,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts between numerical types that may + /// Checks for casts between numeric types that may /// truncate large values. This is expected behavior, so the cast is `Allow` by /// default. It suggests user either explicitly ignore the lint, /// or use `try_from()` and handle the truncation, default, or panic explicitly. @@ -120,17 +120,16 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for casts from an unsigned type to a signed type of - /// the same size, or possibly smaller due to target dependent integers. - /// Performing such a cast is a 'no-op' for the compiler, i.e., nothing is - /// changed at the bit level, and the binary representation of the value is + /// the same size, or possibly smaller due to target-dependent integers. + /// Performing such a cast is a no-op for the compiler (that is, nothing is + /// changed at the bit level), and the binary representation of the value is /// reinterpreted. This can cause wrapping if the value is too big /// for the target signed type. However, the cast works as defined, so this lint /// is `Allow` by default. /// /// ### Why is this bad? /// While such a cast is not bad in itself, the results can - /// be surprising when this is not the intended behavior, as demonstrated by the - /// example below. + /// be surprising when this is not the intended behavior: /// /// ### Example /// ```no_run @@ -144,16 +143,16 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts between numerical types that may - /// be replaced by safe conversion functions. + /// Checks for casts between numeric types that can be replaced by safe + /// conversion functions. /// /// ### Why is this bad? - /// Rust's `as` keyword will perform many kinds of - /// conversions, including silently lossy conversions. Conversion functions such - /// as `i32::from` will only perform lossless conversions. Using the conversion - /// functions prevents conversions from turning into silent lossy conversions if - /// the types of the input expressions ever change, and make it easier for - /// people reading the code to know that the conversion is lossless. + /// Rust's `as` keyword will perform many kinds of conversions, including + /// silently lossy conversions. Conversion functions such as `i32::from` + /// will only perform lossless conversions. Using the conversion functions + /// prevents conversions from becoming silently lossy if the input types + /// ever change, and makes it clear for people reading the code that the + /// conversion is lossless. /// /// ### Example /// ```no_run @@ -177,19 +176,21 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts to the same type, casts of int literals to integer types, casts of float - /// literals to float types and casts between raw pointers without changing type or constness. + /// Checks for casts to the same type, casts of int literals to integer + /// types, casts of float literals to float types, and casts between raw + /// pointers that don't change type or constness. /// /// ### Why is this bad? /// It's just unnecessary. /// /// ### Known problems - /// When the expression on the left is a function call, the lint considers the return type to be - /// a type alias if it's aliased through a `use` statement - /// (like `use std::io::Result as IoResult`). It will not lint such cases. + /// When the expression on the left is a function call, the lint considers + /// the return type to be a type alias if it's aliased through a `use` + /// statement (like `use std::io::Result as IoResult`). It will not lint + /// such cases. /// - /// This check is also rather primitive. It will only work on primitive types without any - /// intermediate references, raw pointers and trait objects may or may not work. + /// This check will only work on primitive types without any intermediate + /// references: raw pointers and trait objects may or may not work. /// /// ### Example /// ```no_run @@ -211,17 +212,17 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts, using `as` or `pointer::cast`, - /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer + /// Checks for casts, using `as` or `pointer::cast`, from a + /// less strictly aligned pointer to a more strictly aligned pointer. /// /// ### Why is this bad? - /// Dereferencing the resulting pointer may be undefined - /// behavior. + /// Dereferencing the resulting pointer may be undefined behavior. /// /// ### Known problems - /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar - /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like - /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. + /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) or + /// similar on the resulting pointer is fine. Is over-zealous: casts with + /// manual alignment checks or casts like `u64` -> `u8` -> `u16` can be + /// fine. Miri is able to do a more in-depth analysis. /// /// ### Example /// ```no_run @@ -234,20 +235,21 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub CAST_PTR_ALIGNMENT, pedantic, - "cast from a pointer to a more-strictly-aligned pointer" + "cast from a pointer to a more strictly aligned pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts of function pointers to something other than usize + /// Checks for casts of function pointers to something other than `usize`. /// /// ### Why is this bad? - /// Casting a function pointer to anything other than usize/isize is not portable across - /// architectures, because you end up losing bits if the target type is too small or end up with a - /// bunch of extra bits that waste space and add more instructions to the final binary than - /// strictly necessary for the problem + /// Casting a function pointer to anything other than `usize`/`isize` is + /// not portable across architectures. If the target type is too small the + /// address would be truncated, and target types larger than `usize` are + /// unnecessary. /// - /// Casting to isize also doesn't make sense since there are no signed addresses. + /// Casting to `isize` also doesn't make sense, since addresses are never + /// signed. /// /// ### Example /// ```no_run @@ -263,17 +265,17 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub FN_TO_NUMERIC_CAST, style, - "casting a function pointer to a numeric type other than usize" + "casting a function pointer to a numeric type other than `usize`" } declare_clippy_lint! { /// ### What it does /// Checks for casts of a function pointer to a numeric type not wide enough to - /// store address. + /// store an address. /// /// ### Why is this bad? /// Such a cast discards some bits of the function's address. If this is intended, it would be more - /// clearly expressed by casting to usize first, then casting the usize to the intended type (with + /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with /// a comment) to perform the truncation. /// /// ### Example @@ -306,7 +308,7 @@ declare_clippy_lint! { /// ### Why restrict this? /// Casting a function pointer to an integer can have surprising results and can occur /// accidentally if parentheses are omitted from a function call. If you aren't doing anything - /// low-level with function pointers then you can opt-out of casting functions to integers in + /// low-level with function pointers then you can opt out of casting functions to integers in /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function /// pointer casts in your code. /// @@ -349,8 +351,8 @@ declare_clippy_lint! { /// ### Why is this bad? /// In general, casting values to smaller types is /// error-prone and should be avoided where possible. In the particular case of - /// converting a character literal to u8, it is easy to avoid by just using a - /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter + /// converting a character literal to `u8`, it is easy to avoid by just using a + /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter /// than `'a' as u8`. /// /// ### Example @@ -371,12 +373,13 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `as` casts between raw pointers without changing its mutability, - /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// Checks for `as` casts between raw pointers that don't change their + /// constness, namely `*const T` to `*const U` and `*mut T` to `*mut U`. /// /// ### Why is this bad? - /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because - /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// Though `as` casts between raw pointers are not terrible, + /// `pointer::cast` is safer because it cannot accidentally change the + /// pointer's mutability, nor cast the pointer to other types like `usize`. /// /// ### Example /// ```no_run @@ -395,12 +398,12 @@ declare_clippy_lint! { #[clippy::version = "1.51.0"] pub PTR_AS_PTR, pedantic, - "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" + "casting using `as` between raw pointers that doesn't change their constness, where `pointer::cast` could take the place of `as`" } declare_clippy_lint! { /// ### What it does - /// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to + /// Checks for `as` casts between raw pointers that change their constness, namely `*const T` to /// `*mut T` and `*mut T` to `*const T`. /// /// ### Why is this bad? @@ -423,12 +426,12 @@ declare_clippy_lint! { #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, pedantic, - "casting using `as` from and to raw pointers to change constness when specialized methods apply" + "casting using `as` on raw pointers to change constness when specialized methods apply" } declare_clippy_lint! { /// ### What it does - /// Checks for casts from an enum type to an integral type which will definitely truncate the + /// Checks for casts from an enum type to an integral type that will definitely truncate the /// value. /// /// ### Why is this bad? @@ -442,7 +445,7 @@ declare_clippy_lint! { #[clippy::version = "1.61.0"] pub CAST_ENUM_TRUNCATION, suspicious, - "casts from an enum type to an integral type which will truncate the value" + "casts from an enum type to an integral type that will truncate the value" } declare_clippy_lint! { @@ -621,7 +624,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer. /// /// ### Why is this bad? /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 2c168405ee260..86c5f6b9f0ba0 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -92,7 +92,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { cx, PTR_AS_PTR, expr.span, - "`as` casting between raw pointers without changing its mutability", + "`as` casting between raw pointers without changing their constness", help, final_suggestion, app, diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs new file mode 100644 index 0000000000000..b54f392bf2fad --- /dev/null +++ b/clippy_lints/src/cfg_not_test.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::NestedMetaItem; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`) + /// + /// ### Why is this bad? + /// This may give the false impression that a codebase has 100% coverage, yet actually has untested code. + /// Enabling this also guards against excessive mockery as well, which is an anti-pattern. + /// + /// ### Example + /// ```rust + /// # fn important_check() {} + /// #[cfg(not(test))] + /// important_check(); // I'm not actually tested, but not including me will falsely increase coverage! + /// ``` + /// Use instead: + /// ```rust + /// # fn important_check() {} + /// important_check(); + /// ``` + #[clippy::version = "1.73.0"] + pub CFG_NOT_TEST, + restriction, + "enforce against excluding code from test builds" +} + +declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); + +impl EarlyLintPass for CfgNotTest { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { + if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) { + span_lint_and_then( + cx, + CFG_NOT_TEST, + attr.span, + "code is excluded from test builds", + |diag| { + diag.help("consider not excluding any code from test builds"); + diag.note_once("this could increase code coverage despite not actually being tested"); + }, + ); + } + } +} + +fn contains_not_test(list: Option<&[NestedMetaItem]>, not: bool) -> bool { + list.is_some_and(|list| { + list.iter().any(|item| { + item.ident().is_some_and(|ident| match ident.name { + rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not), + rustc_span::sym::test => not, + _ => contains_not_test(item.meta_item_list(), not), + }) + }) + }) +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 638de5e818c8e..eabc67601a2f1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -8,8 +8,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, @@ -73,6 +71,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, crate::box_default::BOX_DEFAULT_INFO, + crate::byte_char_slices::BYTE_CHAR_SLICES_INFO, crate::cargo::CARGO_COMMON_METADATA_INFO, crate::cargo::LINT_GROUPS_PRIORITY_INFO, crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO, @@ -103,6 +102,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::REF_AS_PTR_INFO, crate::casts::UNNECESSARY_CAST_INFO, crate::casts::ZERO_PTR_INFO, + crate::cfg_not_test::CFG_NOT_TEST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, @@ -313,6 +313,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, crate::manual_retain::MANUAL_RETAIN_INFO, + crate::manual_rotate::MANUAL_ROTATE_INFO, crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO, crate::manual_string_new::MANUAL_STRING_NEW_INFO, crate::manual_strip::MANUAL_STRIP_INFO, @@ -503,6 +504,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO, crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, + crate::missing_const_for_thread_local::MISSING_CONST_FOR_THREAD_LOCAL_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO, @@ -585,12 +587,12 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::VERBOSE_BIT_MASK_INFO, crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO, crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO, - crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO, crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO, crate::panic_unimplemented::PANIC_INFO, crate::panic_unimplemented::TODO_INFO, crate::panic_unimplemented::UNIMPLEMENTED_INFO, crate::panic_unimplemented::UNREACHABLE_INFO, + crate::panicking_overflow_checks::PANICKING_OVERFLOW_CHECKS_INFO, crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO, crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO, crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, @@ -644,6 +646,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO, crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, crate::serde_api::SERDE_API_MISUSE_INFO, + crate::set_contains_or_insert::SET_CONTAINS_OR_INSERT_INFO, crate::shadow::SHADOW_REUSE_INFO, crate::shadow::SHADOW_SAME_INFO, crate::shadow::SHADOW_UNRELATED_INFO, @@ -679,7 +682,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, - crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 2b3f4854255cf..72fa05be3cc60 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_drop, is_copy}; -use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro}; +use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // Avoid cases already linted by `field_reassign_with_default` && !self.reassigned_linted.contains(&expr.span) && let ExprKind::Call(path, ..) = expr.kind - && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && !in_automatically_derived(cx.tcx, expr.hir_id) && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::default_fn, def_id) @@ -113,9 +113,9 @@ impl<'tcx> LateLintPass<'tcx> for Default { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding let stmts_head = match block.stmts { + [] | [_] => return, // Skip the last statement since there cannot possibly be any following statements that re-assign fields. - [head @ .., _] if !head.is_empty() => head, - _ => return, + [head @ .., _] => head, }; for (stmt_idx, stmt) in stmts_head.iter().enumerate() { // find all binding statements like `let mut _ = T::default()` where `T::default()` is the @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { let (local, variant, binding_name, binding_type, span) = if let StmtKind::Let(local) = stmt.kind // only take `let ...` statements && let Some(expr) = local.init - && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && !in_automatically_derived(cx.tcx, expr.hir_id) && !expr.span.from_expansion() // only take bindings to identifiers && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index ff631909bcb58..9af73db6849f4 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -50,6 +50,8 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { let hir = cx.tcx.hir(); + // NOTE: this is different from `clippy_utils::is_inside_always_const_context`. + // Inline const supports type inference. let is_parent_const = matches!( hir.body_const_context(hir.body_owner_def_id(body.id())), Some(ConstContext::Const { inline: false } | ConstContext::Static(_)) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a115f8d063140..253f9959e13e7 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; use clippy_utils::{ - expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, + expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, + ExprUseNode, }; use core::mem; use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS}; @@ -175,6 +176,7 @@ struct StateData<'tcx> { adjusted_ty: Ty<'tcx>, } +#[derive(Debug)] struct DerefedBorrow { count: usize, msg: &'static str, @@ -182,6 +184,7 @@ struct DerefedBorrow { for_field_access: Option, } +#[derive(Debug)] enum State { // Any number of deref method calls. DerefMethod { @@ -744,7 +747,7 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum TyCoercionStability { Deref, Reborrow, @@ -1042,16 +1045,28 @@ fn report<'tcx>( return; } - let (prefix, precedence) = if let Some(mutability) = mutability - && !typeck.expr_ty(expr).is_ref() + let ty = typeck.expr_ty(expr); + + // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst). + if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() + && dst.is_slice() { - let prefix = match mutability { - Mutability::Not => "&", - Mutability::Mut => "&mut ", - }; - (prefix, PREC_PREFIX) - } else { - ("", 0) + let (src, n_src_refs) = peel_middle_ty_refs(ty); + if n_src_refs >= 2 && src.is_array() { + return; + } + } + + let (prefix, precedence) = match mutability { + Some(mutability) if !ty.is_ref() => { + let prefix = match mutability { + Mutability::Not => "&", + Mutability::Mut => "&mut ", + }; + (prefix, PREC_PREFIX) + }, + None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0), + _ => ("", 0), }; span_lint_hir_and_then( cx, diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 9de879604e2ec..38fe687f7ccfe 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,6 +1,6 @@ use clippy_config::types::DisallowedPath; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -83,26 +83,26 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let uncalled_path = if let Some(parent) = get_parent_expr(cx, expr) - && let ExprKind::Call(receiver, _) = parent.kind - && receiver.hir_id == expr.hir_id - { - None - } else { - path_def_id(cx, expr) + let (id, span) = match &expr.kind { + ExprKind::Path(path) + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = + cx.qpath_res(path, expr.hir_id) => + { + (id, expr.span) + }, + ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { + (id, name.ident.span) + }, + _ => return, }; - let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else { - return; - }; - let conf = match self.disallowed.get(&def_id) { - Some(&index) => &self.conf_disallowed[index], - None => return, - }; - let msg = format!("use of a disallowed method `{}`", conf.path()); - span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, msg, |diag| { - if let Some(reason) = conf.reason() { - diag.note(reason); - } - }); + if let Some(&index) = self.disallowed.get(&id) { + let conf = &self.conf_disallowed[index]; + let msg = format!("use of a disallowed method `{}`", conf.path()); + span_lint_and_then(cx, DISALLOWED_METHODS, span, msg, |diag| { + if let Some(reason) = conf.reason() { + diag.note(reason); + } + }); + } } } diff --git a/clippy_lints/src/disallowed_names.rs b/clippy_lints/src/disallowed_names.rs index 2afbf184117e1..58809604c373e 100644 --- a/clippy_lints/src/disallowed_names.rs +++ b/clippy_lints/src/disallowed_names.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_test_module_or_function; +use clippy_utils::is_in_test; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Item, Pat, PatKind}; +use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -27,52 +27,30 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedNames { disallow: FxHashSet, - test_modules_deep: u32, } impl DisallowedNames { pub fn new(disallowed_names: &[String]) -> Self { Self { disallow: disallowed_names.iter().cloned().collect(), - test_modules_deep: 0, } } - - fn in_test_module(&self) -> bool { - self.test_modules_deep != 0 - } } impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); impl<'tcx> LateLintPass<'tcx> for DisallowedNames { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(cx.tcx, item) { - self.test_modules_deep = self.test_modules_deep.saturating_add(1); - } - } - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - // Check whether we are under the `test` attribute. - if self.in_test_module() { - return; - } - - if let PatKind::Binding(.., ident, _) = pat.kind { - if self.disallow.contains(&ident.name.to_string()) { - span_lint( - cx, - DISALLOWED_NAMES, - ident.span, - format!("use of a disallowed/placeholder name `{}`", ident.name), - ); - } - } - } - - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(cx.tcx, item) { - self.test_modules_deep = self.test_modules_deep.saturating_sub(1); + if let PatKind::Binding(.., ident, _) = pat.kind + && self.disallow.contains(&ident.name.to_string()) + && !is_in_test(cx.tcx, pat.hir_id) + { + span_lint( + cx, + DISALLOWED_NAMES, + ident.span, + format!("use of a disallowed/placeholder name `{}`", ident.name), + ); } } } diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index a995f06fb73dc..5ce11900adf8f 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -82,30 +82,25 @@ impl EarlyLintPass for DisallowedScriptIdents { // Note: `symbol.as_str()` is an expensive operation, thus should not be called // more than once for a single symbol. let symbol_str = symbol.as_str(); - if symbol_str.is_ascii() { - continue; - } - for c in symbol_str.chars() { - // We want to iterate through all the scripts associated with this character - // and check whether at least of one scripts is in the whitelist. - let forbidden_script = c - .script_extension() - .iter() - .find(|script| !self.whitelist.contains(script)); - if let Some(script) = forbidden_script { - span_lint( - cx, - DISALLOWED_SCRIPT_IDENTS, - span, - format!( - "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", - script.full_name() - ), - ); - // We don't want to spawn warning multiple times over a single identifier. - break; - } + // Check if any character in the symbol is not part of any allowed script. + // Fast path for ascii-only idents. + if !symbol_str.is_ascii() + && let Some(script) = symbol_str.chars().find_map(|c| { + c.script_extension() + .iter() + .find(|script| !self.whitelist.contains(script)) + }) + { + span_lint( + cx, + DISALLOWED_SCRIPT_IDENTS, + span, + format!( + "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", + script.full_name() + ), + ); } } } diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index 38bc58a550196..bd1cc46e1850e 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -22,6 +22,7 @@ pub(super) fn check( range: Range, mut span: Span, containers: &[super::Container], + line_break_span: Span, ) { if doc[range.clone()].contains('\t') { // We don't do tab stops correctly. @@ -46,11 +47,35 @@ pub(super) fn check( .sum(); if ccount < blockquote_level || lcount < list_indentation { let msg = if ccount < blockquote_level { - "doc quote missing `>` marker" + "doc quote line without `>` marker" } else { - "doc list item missing indentation" + "doc list item without indentation" }; span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { + let snippet = clippy_utils::source::snippet(cx, line_break_span, ""); + if snippet.chars().filter(|&c| c == '\n').count() > 1 + && let Some(doc_comment_start) = snippet.rfind('\n') + && let doc_comment = snippet[doc_comment_start..].trim() + && (doc_comment == "///" || doc_comment == "//!") + { + // suggest filling in a blank line + diag.span_suggestion_with_style( + line_break_span.shrink_to_lo(), + "if this should be its own paragraph, add a blank doc comment line", + format!("\n{doc_comment}"), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + if ccount > 0 || blockquote_level > 0 { + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + } else { + let indent = list_indentation - lcount; + diag.help(format!( + "if this is intended to be part of the list, indent {indent} spaces" + )); + } + return; + } if ccount == 0 && blockquote_level == 0 { // simpler suggestion style for indentation let indent = list_indentation - lcount; diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 3e210fd153bf5..a2a1a51920f35 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ - Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, + Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, + TaskListMarker, Text, }; use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; @@ -747,7 +748,8 @@ fn check_doc<'a, Events: Iterator, Range in_footnote_definition = true, End(TagEnd::FootnoteDefinition) => in_footnote_definition = false, - Start(_) | End(_) => (), // We don't care about other tags + Start(_) | End(_) // We don't care about other tags + | TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (), SoftBreak | HardBreak => { if !containers.is_empty() && let Some((next_event, next_range)) = events.peek() @@ -762,13 +764,24 @@ fn check_doc<'a, Events: Iterator, Range (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; - ticks_unbalanced |= text.contains('`') && !in_code; + let range_ = range.clone(); + ticks_unbalanced |= text.contains('`') + && !in_code + && doc[range.clone()].bytes().enumerate().any(|(i, c)| { + // scan the markdown source code bytes for backquotes that aren't preceded by backslashes + // - use bytes, instead of chars, to avoid utf8 decoding overhead (special chars are ascii) + // - relevant backquotes are within doc[range], but backslashes are not, because they're not + // actually part of the rendered text (pulldown-cmark doesn't emit any events for escapes) + // - if `range_.start + i == 0`, then `range_.start + i - 1 == -1`, and since we're working in + // usize, that would underflow and maybe panic + c == b'`' && (range_.start + i == 0 || doc.as_bytes().get(range_.start + i - 1) != Some(&b'\\')) + }); if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `` // Which are represented as a link to "http://example.com" with diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index bb766e9633872..99328e3e643f8 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`. /// /// ### Why restrict this? - /// To ensure use of big endian or the target’s endianness rather than little endian. + /// To ensure use of big-endian or the target’s endianness rather than little-endian. /// /// ### Example /// ```rust,ignore @@ -51,7 +51,7 @@ declare_clippy_lint! { /// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`. /// /// ### Why restrict this? - /// To ensure use of little endian or the target’s endianness rather than big endian. + /// To ensure use of little-endian or the target’s endianness rather than big-endian. /// /// ### Example /// ```rust,ignore diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index 6fb38a0d6dd84..cf85c74e688d2 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_test; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -41,7 +41,7 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { if let FnKind::ItemFn(_, generics, _) = kind && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() - && !is_in_test_function(cx.tcx, hir_id) + && !is_in_test(cx.tcx, hir_id) { for param in generics.params { if param.is_impl_trait() { @@ -59,7 +59,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { && of_trait.is_none() && let body = cx.tcx.hir().body(body_id) && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() - && !is_in_test_function(cx.tcx, impl_item.hir_id()) + && !is_in_test(cx.tcx, impl_item.hir_id()) { for param in impl_item.generics.params { if param.is_impl_trait() { @@ -75,7 +75,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, && let hir::Node::Item(item) = cx.tcx.parent_hir_node(trait_item.hir_id()) // ^^ (Will always be a trait) && !item.vis_span.is_empty() // Is public - && !is_in_test_function(cx.tcx, trait_item.hir_id()) + && !is_in_test(cx.tcx, trait_item.hir_id()) { for param in trait_item.generics.params { if param.is_impl_trait() { diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 35b4481bfee7e..5c63d48adaf0b 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_test; use rustc_attr::{StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; @@ -88,7 +88,7 @@ impl IncompatibleMsrv { return; } let version = self.get_def_id_version(cx.tcx, def_id); - if self.msrv.meets(version) || is_in_test_function(cx.tcx, node) { + if self.msrv.meets(version) || is_in_test(cx.tcx, node) { return; } if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 9aedf5ec7e85f..ec6174bc0301a 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -91,10 +91,6 @@ declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_S impl<'tcx> LateLintPass<'tcx> for InherentToString { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if impl_item.span.from_expansion() { - return; - } - // Check if item is a method called `to_string` and has a parameter 'self' if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // #11201 @@ -106,6 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { && decl.implicit_self.has_implicit_self() && decl.inputs.len() == 1 && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + && !impl_item.span.from_expansion() // Check if return type is String && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) // Filters instances of to_string which are required by a trait diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 1c8fd0a27f98f..7f183bb601eb4 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,13 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::borrow::Cow; -use std::cmp::Reverse; -use std::collections::BinaryHeap; declare_clippy_lint! { /// ### What it does @@ -44,38 +43,56 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]); impl<'tcx> LateLintPass<'tcx> for NumberedFields { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Struct(path, fields, None) = e.kind { - if !fields.is_empty() - && !e.span.from_expansion() - && fields - .iter() - .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) - && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) - { - let expr_spans = fields - .iter() - .map(|f| (Reverse(f.ident.as_str().parse::().unwrap()), f.expr.span)) - .collect::>(); - let mut appl = Applicability::MachineApplicable; - let snippet = format!( - "{}({})", - snippet_with_applicability(cx, path.span(), "..", &mut appl), - expr_spans - .into_iter_sorted() - .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0) - .intersperse(Cow::Borrowed(", ")) - .collect::() - ); - span_lint_and_sugg( - cx, - INIT_NUMBERED_FIELDS, - e.span, - "used a field initializer for a tuple struct", - "try", - snippet, - appl, - ); - } + if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind + // If the first character of any field is a digit it has to be a tuple. + && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit) + // Type aliases can't be used as functions. + && !matches!( + cx.qpath_res(path, e.hir_id), + Res::Def(DefKind::TyAlias | DefKind::AssocTy, _) + ) + // This is the only syntax macros can use that works for all struct types. + && !e.span.from_expansion() + && let mut has_side_effects = false + && let Ok(mut expr_spans) = fields + .iter() + .map(|f| { + has_side_effects |= f.expr.can_have_side_effects(); + f.ident.as_str().parse::().map(|x| (x, f.expr.span)) + }) + .collect::, _>>() + // We can only reorder the expressions if there are no side effects. + && (!has_side_effects || expr_spans.is_sorted_by_key(|&(idx, _)| idx)) + { + span_lint_and_then( + cx, + INIT_NUMBERED_FIELDS, + e.span, + "used a field initializer for a tuple struct", + |diag| { + if !has_side_effects { + // We already checked the order if there are side effects. + expr_spans.sort_by_key(|&(idx, _)| idx); + } + let mut app = Applicability::MachineApplicable; + diag.span_suggestion( + e.span, + "use tuple initialization", + format!( + "{}({})", + snippet_with_applicability(cx, path.span(), "..", &mut app), + expr_spans + .into_iter() + .map( + |(_, span)| snippet_with_context(cx, span, SyntaxContext::root(), "..", &mut app).0 + ) + .intersperse(Cow::Borrowed(", ")) + .collect::() + ), + app, + ); + }, + ); } } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 860258fd030e0..5657c58bb0a47 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -2,12 +2,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::DiagExt; -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Symbol}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -34,27 +33,23 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - check_attrs(cx, item.ident.name, attrs); + if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind + && let Some(attr) = cx + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .find(|a| a.has_name(sym::inline)) + { + span_lint_and_then( + cx, + INLINE_FN_WITHOUT_BODY, + attr.span, + format!("use of `#[inline]` on trait method `{}` which has no body", item.ident), + |diag| { + diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); + }, + ); } } } - -fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { - for attr in attrs { - if !attr.has_name(sym::inline) { - continue; - } - - span_lint_and_then( - cx, - INLINE_FN_WITHOUT_BODY, - attr.span, - format!("use of `#[inline]` on trait method `{name}` which has no body"), - |diag| { - diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); - }, - ); - } -} diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 10b00f632bb00..5fe152d1e3011 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -85,16 +85,19 @@ impl LateLintPass<'_> for InstantSubtraction { lhs, rhs, ) = expr.kind + && let typeck = cx.typeck_results() + && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) { + let rhs_ty = typeck.expr_ty(rhs); + if is_instant_now_call(cx, lhs) - && is_an_instant(cx, rhs) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); - } else if !expr.span.from_expansion() + } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() && self.msrv.meets(msrvs::TRY_FROM) - && is_an_instant(cx, lhs) - && is_a_duration(cx, rhs) { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } @@ -115,16 +118,6 @@ fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { } } -fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let expr_ty = cx.typeck_results().expr_ty(expr); - ty::is_type_diagnostic_item(cx, expr_ty, sym::Instant) -} - -fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let expr_ty = cx.typeck_results().expr_ty(expr); - ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) -} - fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 39223c20470b5..a88d8e24fda8d 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -54,36 +54,35 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl LateLintPass<'_> for ItemsAfterStatements { fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { - if in_external_macro(cx.sess(), block.span) { - return; - } - - // skip initial items - let stmts = block - .stmts - .iter() - .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..))); - - // lint on all further items - for stmt in stmts { - if let StmtKind::Item(item_id) = stmt.kind { - let item = cx.tcx.hir().item(item_id); - if in_external_macro(cx.sess(), item.span) || !item.span.eq_ctxt(block.span) { - return; - } - if let ItemKind::Macro(..) = item.kind { - // do not lint `macro_rules`, but continue processing further statements - continue; - } - span_lint_hir( - cx, - ITEMS_AFTER_STATEMENTS, - item.hir_id(), - item.span, - "adding items after statements is confusing, since items exist from the \ - start of the scope", - ); - } + if block.stmts.len() > 1 { + let ctxt = block.span.ctxt(); + let mut in_external = None; + block + .stmts + .iter() + .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..))) + .filter_map(|stmt| match stmt.kind { + StmtKind::Item(id) => Some(cx.tcx.hir().item(id)), + _ => None, + }) + // Ignore macros since they can only see previously defined locals. + .filter(|item| !matches!(item.kind, ItemKind::Macro(..))) + // Stop linting if macros define items. + .take_while(|item| item.span.ctxt() == ctxt) + // Don't use `next` due to the complex filter chain. + .for_each(|item| { + // Only do the macro check once, but delay it until it's needed. + if !*in_external.get_or_insert_with(|| in_external_macro(cx.sess(), block.span)) { + span_lint_hir( + cx, + ITEMS_AFTER_STATEMENTS, + item.hir_id(), + item.span, + "adding items after statements is confusing, since items exist from the \ + start of the scope", + ); + } + }); } } } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 1b5f1b499475f..ba0cd5d6eb359 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -43,30 +43,27 @@ declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - let name = item.ident.name.as_str(); - if matches!(name, "iter" | "iter_mut") { - if let TraitItemKind::Fn(fn_sig, _) = &item.kind { - check_sig(cx, name, fn_sig, item.owner_id.def_id); - } + if let TraitItemKind::Fn(fn_sig, _) = &item.kind + && matches!(item.ident.name, sym::iter | sym::iter_mut) + { + check_sig(cx, item.ident.name, fn_sig, item.owner_id.def_id); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { - let name = item.ident.name.as_str(); - if matches!(name, "iter" | "iter_mut") + if let ImplItemKind::Fn(fn_sig, _) = &item.kind + && matches!(item.ident.name, sym::iter | sym::iter_mut) && !matches!( cx.tcx.parent_hir_node(item.hir_id()), Node::Item(Item { kind: ItemKind::Impl(i), .. }) if i.of_trait.is_some() ) { - if let ImplItemKind::Fn(fn_sig, _) = &item.kind { - check_sig(cx, name, fn_sig, item.owner_id.def_id); - } + check_sig(cx, item.ident.name, fn_sig, item.owner_id.def_id); } } } -fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { +fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { let ret_ty = cx .tcx diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 6b03f2597b083..1e6404190d3cf 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -125,13 +125,13 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { - if !in_external_macro(cx.sess(), item.span) - && let ItemKind::Impl(imp) = item.kind + if let ItemKind::Impl(imp) = item.kind && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind && let Some(trait_ref) = imp.of_trait && trait_ref .trait_def_id() .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did)) + && !in_external_macro(cx.sess(), item.span) && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() && let expected_method_name = match mtbl { Mutability::Mut => sym::iter_mut, diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 7f8197c0cc01a..b18ab625e6091 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -46,12 +46,12 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !item.span.from_expansion() - && let ItemKind::Const(_, generics, _) = &item.kind + if let ItemKind::Const(_, generics, _) = &item.kind // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. && generics.params.is_empty() && !generics.has_where_clause_predicates + && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 0bf7389ef9cc8..85daadcc53734 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -77,17 +77,12 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - if in_external_macro(cx.tcx.sess, item.span) { - return; - } - if let ItemKind::Enum(ref def, _) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let ty::Adt(adt, subst) = ty.kind() else { - panic!("already checked whether this is an enum") - }; - if adt.variants().len() <= 1 { - return; - } + if let ItemKind::Enum(ref def, _) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let ty::Adt(adt, subst) = ty.kind() + && adt.variants().len() > 1 + && !in_external_macro(cx.tcx.sess, item.span) + { let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 07488a512a37e..602227e42490a 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -54,29 +54,26 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]); impl<'tcx> LateLintPass<'tcx> for LargeFuture { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) { - return; - } - if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [expr, ..]) = expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind - && let ty = cx.typeck_results().expr_ty(expr) - && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() - && implements_trait(cx, ty, future_trait_def_id, &[]) - && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) - && let size = layout.layout.size() - && size >= Size::from_bytes(self.future_size_threshold) - { - span_lint_and_sugg( - cx, - LARGE_FUTURES, - expr.span, - format!("large future with a size of {} bytes", size.bytes()), - "consider `Box::pin` on it", - format!("Box::pin({})", snippet(cx, expr.span, "..")), - Applicability::Unspecified, - ); - } + if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(func, [arg, ..]) = scrutinee.kind + && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind + && !expr.span.from_expansion() + && let ty = cx.typeck_results().expr_ty(arg) + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + && implements_trait(cx, ty, future_trait_def_id, &[]) + && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) + && let size = layout.layout.size() + && size >= Size::from_bytes(self.future_size_threshold) + { + span_lint_and_sugg( + cx, + LARGE_FUTURES, + arg.span, + format!("large future with a size of {} bytes", size.bytes()), + "consider `Box::pin` on it", + format!("Box::pin({})", snippet(cx, arg.span, "..")), + Applicability::Unspecified, + ); } } } diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 07efee159aab4..2688283a6ce8d 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -52,24 +51,19 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(cx, expr) - && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) - || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) - && let ExprKind::Lit(lit) = &expr.kind - { - let len = match &lit.node { + if let ExprKind::Lit(lit) = &expr.kind + && let len = match &lit.node { // include_bytes LitKind::ByteStr(bstr, _) => bstr.len(), // include_str LitKind::Str(sym, _) => sym.as_str().len(), _ => return, - }; - - if len as u64 <= self.max_file_size { - return; } - + && len as u64 > self.max_file_size + && let Some(macro_call) = root_macro_call_first_node(cx, expr) + && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) + { span_lint_and_note( cx, LARGE_INCLUDE_FILE, diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index eadfeb6e34181..a08b40bef3722 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -48,15 +48,11 @@ impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - let Self { msrv } = self; - - if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), item.span) { - return; - } - // Integer modules are "TBD" deprecated, and the contents are too, // so lint on the `use` statement directly. if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind + && self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) + && !in_external_macro(cx.sess(), item.span) && let Some(def_id) = path.res[0].opt_def_id() { let module = if is_integer_module(cx, def_id) { @@ -103,12 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - let Self { msrv } = self; - - if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), expr.span) { - return; - } - let ExprKind::Path(qpath) = expr.kind else { + let ExprKind::Path(qpath) = &expr.kind else { return; }; @@ -129,10 +120,10 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { ) // `::xxx_value` check } else if let QPath::TypeRelative(_, last_segment) = qpath - && let Some(def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() - && is_integer_method(cx, def_id) + && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() && let Some(par_expr) = get_parent_expr(cx, expr) - && let ExprKind::Call(_, _) = par_expr.kind + && let ExprKind::Call(_, []) = par_expr.kind + && is_integer_method(cx, def_id) { let name = last_segment.ident.name.as_str(); @@ -145,19 +136,20 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { return; }; - if is_from_proc_macro(cx, expr) { - return; + if self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) + && !in_external_macro(cx.sess(), expr.span) + && !is_from_proc_macro(cx, expr) + { + span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { + diag.span_suggestion_with_style( + span, + "use the associated constant instead", + sugg, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + }); } - - span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { - diag.span_suggestion_with_style( - span, - "use the associated constant instead", - sugg, - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); - }); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 57e0a7aa2c7e9..4c737371bd238 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -121,11 +121,9 @@ declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMP impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.span.from_expansion() { - return; - } - - if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind { + if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind + && !item.span.from_expansion() + { check_trait_items(cx, item, trait_items); } } @@ -162,17 +160,14 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - if let ExprKind::Let(lt) = expr.kind - && has_is_empty(cx, lt.init) && match lt.pat.kind { PatKind::Slice([], None, []) => true, PatKind::Lit(lit) if is_empty_string(lit) => true, _ => false, } + && !expr.span.from_expansion() + && has_is_empty(cx, lt.init) { let mut applicability = Applicability::MachineApplicable; @@ -190,7 +185,9 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { ); } - if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind + && !expr.span.from_expansion() + { // expr.span might contains parenthesis, see issue #10529 let actual_span = span_without_enclosing_paren(cx, expr.span); match cmp { diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index a65cb3f4dfb44..0e488cee6b74e 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -58,12 +58,10 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]); impl<'tcx> LateLintPass<'tcx> for LetIfSeq { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let mut it = block.stmts.iter().peekable(); - while let Some(stmt) = it.next() { - if let Some(expr) = it.peek() - && let hir::StmtKind::Let(local) = stmt.kind + for [stmt, next] in block.stmts.array_windows::<2>() { + if let hir::StmtKind::Let(local) = stmt.kind && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind - && let hir::StmtKind::Expr(if_) = expr.kind + && let hir::StmtKind::Expr(if_) = next.kind && let hir::ExprKind::If( hir::Expr { kind: hir::ExprKind::DropTemps(cond), diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 9fd4f509aa473..8fa63f3e8fde0 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -139,9 +139,9 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) - && !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind && let Some(init) = local.init + && !in_external_macro(cx.tcx.sess, local.span) { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 593b29154b426..5a11702d7ce54 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::snippet; +use clippy_utils::is_from_proc_macro; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -25,19 +25,14 @@ declare_clippy_lint! { } declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); -impl LateLintPass<'_> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { - if !in_external_macro(cx.tcx.sess, local.span) - && let Some(ty) = local.ty // Ensure that it has a type defined +impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { + if let Some(ty) = local.ty // Ensure that it has a type defined && let TyKind::Infer = &ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) + && !in_external_macro(cx.tcx.sess, local.span) + && !is_from_proc_macro(cx, ty) { - // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, - // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` - if snippet(cx, ty.span, "_").trim() != "_" { - return; - } - span_lint_and_help( cx, LET_WITH_TYPE_UNDERSCORE, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 32fad0f02ceab..c2dc26d6605bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -4,7 +4,9 @@ #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] +#![feature(is_sorted)] #![feature(iter_intersperse)] +#![feature(iter_partition_in_place)] #![feature(let_chains)] #![cfg_attr(bootstrap, feature(lint_reasons))] #![feature(never_type)] @@ -90,8 +92,10 @@ mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; mod box_default; +mod byte_char_slices; mod cargo; mod casts; +mod cfg_not_test; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; @@ -212,6 +216,7 @@ mod manual_non_exhaustive; mod manual_range_patterns; mod manual_rem_euclid; mod manual_retain; +mod manual_rotate; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; @@ -229,6 +234,7 @@ mod mismatching_type_param_order; mod missing_assert_message; mod missing_asserts_for_indexing; mod missing_const_for_fn; +mod missing_const_for_thread_local; mod missing_doc; mod missing_enforced_import_rename; mod missing_fields_in_debug; @@ -275,9 +281,9 @@ mod only_used_in_recursion; mod operators; mod option_env_unwrap; mod option_if_let_else; -mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod panicking_overflow_checks; mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; @@ -318,6 +324,7 @@ mod self_named_constructors; mod semicolon_block; mod semicolon_if_nothing_returned; mod serde_api; +mod set_contains_or_insert; mod shadow; mod significant_drop_tightening; mod single_call_fn; @@ -339,7 +346,6 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; -mod thread_local_initializer_can_be_made_const; mod to_digit_is_some; mod to_string_trait_impl; mod trailing_empty_array; @@ -639,9 +645,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| { - Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) - }); store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); store.register_late_pass(|_| { Box::::default() @@ -788,7 +791,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); - store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); + store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names))); store.register_late_pass(move |_| { @@ -1024,6 +1027,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| { Box::new(operators::Operators::new( verbose_bit_mask_threshold, @@ -1153,9 +1157,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { behavior: pub_underscore_fields_behavior, }) }); - store.register_late_pass(move |_| { - Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv())) - }); + store + .register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(msrv()))); store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); @@ -1171,6 +1174,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv()))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); + store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); + store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); + store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index d2a140a36a83f..7481543941a6b 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -233,11 +233,9 @@ impl_lint_pass!(LiteralDigitGrouping => [ impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + if let ExprKind::Lit(lit) = expr.kind + && !in_external_macro(cx.sess(), expr.span) + { self.check_lit(cx, lit, expr.span); } } @@ -448,11 +446,9 @@ impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION] impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + if let ExprKind::Lit(lit) = expr.kind + && !in_external_macro(cx.sess(), expr.span) + { self.check_lit(cx, lit, expr.span); } } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 24fc2b4faeacc..d9f6be6dc2b5c 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -50,13 +50,10 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !self.msrv.meets(msrvs::MANUAL_BITS) { - return; - } - if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind && let BinOpKind::Mul = &bin_op.node && !in_external_macro(cx.sess(), expr.span) + && self.msrv.meets(msrvs::MANUAL_BITS) && let ctxt = expr.span.ctxt() && left_expr.span.ctxt() == ctxt && right_expr.span.ctxt() == ctxt diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 89eea0b4456d4..03416ba96de73 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -82,29 +82,26 @@ impl Variant { impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !in_external_macro(cx.sess(), expr.span) - && ( - matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) - || cx.tcx.features().declared(sym!(const_float_classify)) - ) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind + if let ExprKind::Binary(kind, lhs, rhs) = expr.kind && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind && let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind // Checking all possible scenarios using a function would be a hopeless task, as we have // 16 possible alignments of constants/operands. For now, let's use `partition`. - && let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] - .into_iter() - .partition::>, _>(|i| path_to_local(i).is_some()) - && let [first, second] = &*operands - && let Some([const_1, const_2]) = constants - .into_iter() - .map(|i| constant(cx, cx.typeck_results(), i)) - .collect::>>() - .as_deref() + && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] + && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2 + && !in_external_macro(cx.sess(), expr.span) + && ( + matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) + || cx.tcx.features().declared(sym!(const_float_classify)) + ) + && let [first, second, const_1, const_2] = exprs + && let Some(const_1) = constant(cx, cx.typeck_results(), const_1) + && let Some(const_2) = constant(cx, cx.typeck_results(), const_2) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason - && (is_infinity(const_1) && is_neg_infinity(const_2) - || is_neg_infinity(const_1) && is_infinity(const_2)) + && (is_infinity(&const_1) && is_neg_infinity(&const_2) + || is_neg_infinity(&const_1) && is_infinity(&const_2)) && let Some(local_snippet) = snippet_opt(cx, first.span) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 03e4d668dd8fb..ebebdf679a863 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -49,16 +49,14 @@ declare_clippy_lint! { impl<'tcx> QuestionMark { pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) { - if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { - return; - } - if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && local.els.is_none() && local.ty.is_none() && init.span.eq_ctxt(stmt.span) && let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) + && self.msrv.meets(msrvs::LET_ELSE) + && !in_external_macro(cx.sess(), stmt.span) { match if_let_or_match { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else, ..) => { diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index 5732bdda7f2cf..8e8cdc3fb0764 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -47,13 +47,13 @@ impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]); impl LateLintPass<'_> for ManualMainSeparatorStr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) - && let (target, _) = peel_hir_expr_refs(expr) - && is_trait_method(cx, target, sym::ToString) - && let ExprKind::MethodCall(path, receiver, &[], _) = target.kind + let (target, _) = peel_hir_expr_refs(expr); + if let ExprKind::MethodCall(path, receiver, &[], _) = target.kind && path.ident.name == sym::to_string && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let Res::Def(DefKind::Const, receiver_def_id) = path.res + && is_trait_method(cx, target, sym::ToString) + && self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) && match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index d2ac0ad8363e9..73a505fd73ffc 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -97,19 +97,15 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { - return; - } - - if let ast::ItemKind::Struct(variant_data, _) = &item.kind { - let (fields, delimiter) = match variant_data { + if let ast::ItemKind::Struct(variant_data, _) = &item.kind + && let (fields, delimiter) = match variant_data { ast::VariantData::Struct { fields, .. } => (&**fields, '{'), ast::VariantData::Tuple(fields, _) => (&**fields, '('), ast::VariantData::Unit(_) => return, - }; - if fields.len() <= 1 { - return; } + && fields.len() > 1 + && self.msrv.meets(msrvs::NON_EXHAUSTIVE) + { let mut iter = fields.iter().filter_map(|f| match f.vis.kind { VisibilityKind::Public => None, VisibilityKind::Inherited => Some(Ok(f)), diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index ec60de92c4ba2..07d4abbf5cd11 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -76,14 +76,11 @@ impl Num { impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { - if in_external_macro(cx.sess(), pat.span) { - return; - } - // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives // or at least one range if let PatKind::Or(pats) = pat.kind && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) + && !in_external_macro(cx.sess(), pat.span) { let mut min = Num::dummy(i128::MAX); let mut max = Num::dummy(i128::MIN); diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index ab9bca170cf77..b518dc26937c2 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -48,35 +48,30 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]); impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !self.msrv.meets(msrvs::REM_EUCLID) { - return; - } - - if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) { - return; - } - - if in_external_macro(cx.sess(), expr.span) { - return; - } - // (x % c + c) % c - if let ExprKind::Binary(op1, expr1, right) = expr.kind - && op1.node == BinOpKind::Rem + if let ExprKind::Binary(rem_op, rem_lhs, rem_rhs) = expr.kind + && rem_op.node == BinOpKind::Rem + && let ExprKind::Binary(add_op, add_lhs, add_rhs) = rem_lhs.kind + && add_op.node == BinOpKind::Add && let ctxt = expr.span.ctxt() - && expr1.span.ctxt() == ctxt - && let Some(const1) = check_for_unsigned_int_constant(cx, right) - && let ExprKind::Binary(op2, left, right) = expr1.kind - && op2.node == BinOpKind::Add - && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right) - && expr2.span.ctxt() == ctxt - && let ExprKind::Binary(op3, expr3, right) = expr2.kind - && op3.node == BinOpKind::Rem - && let Some(const3) = check_for_unsigned_int_constant(cx, right) + && rem_lhs.span.ctxt() == ctxt + && rem_rhs.span.ctxt() == ctxt + && add_lhs.span.ctxt() == ctxt + && add_rhs.span.ctxt() == ctxt + && !in_external_macro(cx.sess(), expr.span) + && self.msrv.meets(msrvs::REM_EUCLID) + && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id)) + && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) + && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) + && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind + && rem2_op.node == BinOpKind::Rem + && const1 == const2 + && let Some(hir_id) = path_to_local(rem2_lhs) + && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor - && const1 == const2 && const2 == const3 - && let Some(hir_id) = path_to_local(expr3) - && let Node::Pat(_) = cx.tcx.hir_node(hir_id) + && const2 == const3 + && rem2_lhs.span.ctxt() == ctxt + && rem2_rhs.span.ctxt() == ctxt { // Apply only to params or locals with annotated types match cx.tcx.parent_hir_node(hir_id) { @@ -91,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { }; let mut app = Applicability::MachineApplicable; - let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0; + let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0; span_lint_and_sugg( cx, MANUAL_REM_EUCLID, diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 3ddb06a1e08a1..8f7b8abd0c383 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -70,9 +70,8 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]); impl<'tcx> LateLintPass<'tcx> for ManualRetain { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Assign(left_expr, collect_expr, _) = &expr.kind - && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind + && let hir::ExprKind::MethodCall(seg, target_expr, [], _) = &collect_expr.kind && seg.args.is_none() - && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) { diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs new file mode 100644 index 0000000000000..a517a4d507526 --- /dev/null +++ b/clippy_lints/src/manual_rotate.rs @@ -0,0 +1,117 @@ +use std::fmt::Display; + +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// It detects manual bit rotations that could be rewritten using standard + /// functions `rotate_left` or `rotate_right`. + /// + /// ### Why is this bad? + /// + /// Calling the function better conveys the intent. + /// + /// ### Known issues + /// + /// Currently, the lint only catches shifts by constant amount. + /// + /// ### Example + /// ```no_run + /// let x = 12345678_u32; + /// let _ = (x >> 8) | (x << 24); + /// ``` + /// Use instead: + /// ```no_run + /// let x = 12345678_u32; + /// let _ = x.rotate_right(8); + /// ``` + #[clippy::version = "1.81.0"] + pub MANUAL_ROTATE, + style, + "using bit shifts to rotate integers" +} + +declare_lint_pass!(ManualRotate => [MANUAL_ROTATE]); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ShiftDirection { + Left, + Right, +} + +impl Display for ShiftDirection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Left => "rotate_left", + Self::Right => "rotate_right", + }) + } +} + +fn parse_shift<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> { + if let ExprKind::Binary(op, l, r) = expr.kind { + let dir = match op.node { + BinOpKind::Shl => ShiftDirection::Left, + BinOpKind::Shr => ShiftDirection::Right, + _ => return None, + }; + let const_expr = constant(cx, cx.typeck_results(), r)?; + if let Constant::Int(shift) = const_expr { + return Some((dir, shift, l)); + } + } + None +} + +impl LateLintPass<'_> for ManualRotate { + fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Binary(op, l, r) = expr.kind + && let BinOpKind::Add | BinOpKind::BitOr = op.node + && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) + && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) + { + if l_shift_dir == r_shift_dir { + return; + } + if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { + return; + } + let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { + ty::Int(itype) => itype.bit_width(), + ty::Uint(itype) => itype.bit_width(), + _ => return, + }) else { + return; + }; + if l_amount + r_amount == u128::from(bit_width) { + let (shift_function, amount) = if l_amount < r_amount { + (l_shift_dir, l_amount) + } else { + (r_shift_dir, r_amount) + }; + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 1de686dbcb51e..429ee2637c2fa 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -40,11 +40,11 @@ declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION] impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - // Does not apply inside const because size_of_val is not cost in stable. - if !in_constant(cx, expr.hir_id) - && let ExprKind::Binary(ref op, left, right) = expr.kind + if let ExprKind::Binary(ref op, left, right) = expr.kind && BinOpKind::Mul == op.node && !expr.span.from_expansion() + // Does not apply inside const because size_of_val is not cost in stable. + && !in_constant(cx, expr.hir_id) && let Some(receiver) = simplify(cx, left, right) { let ctxt = expr.span.ctxt(); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 45af9f07718d2..6a523ad156472 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -66,14 +66,11 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { - return; - } - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) && let ExprKind::Path(target_path) = &target_arg.kind + && self.msrv.meets(msrvs::STR_STRIP_PREFIX) + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) { let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { StripKind::Prefix diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs index 58b2ebebbf080..f1acc4b217480 100644 --- a/clippy_lints/src/manual_unwrap_or_default.rs +++ b/clippy_lints/src/manual_unwrap_or_default.rs @@ -172,11 +172,10 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { - return; - } - // Call handle only if the expression is `if let` or `match` - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) { + if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) + && !expr.span.from_expansion() + && !in_constant(cx, expr.hir_id) + { handle(cx, if_let_or_match, expr); } } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 9db04b615be7b..2db71b1f7a388 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -253,14 +253,11 @@ fn lint_map_unit_fn( impl<'tcx> LateLintPass<'tcx> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { - if stmt.span.from_expansion() { - return; - } - - if let hir::StmtKind::Semi(expr) = stmt.kind { - if let Some(arglists) = method_chain_args(expr, &["map"]) { - lint_map_unit_fn(cx, stmt, expr, arglists[0]); - } + if let hir::StmtKind::Semi(expr) = stmt.kind + && !stmt.span.from_expansion() + && let Some(arglists) = method_chain_args(expr, &["map"]) + { + lint_map_unit_fn(cx, stmt, expr, arglists[0]); } } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index bf7156cc53ec3..22a299ae3d82f 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1019,6 +1019,7 @@ impl_lint_pass!(Matches => [ ]); impl<'tcx> LateLintPass<'tcx> for Matches { + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) { return; @@ -1037,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { return; } if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) { - significant_drop_in_scrutinee::check(cx, expr, ex, arms, source); + significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source); } collapsible_match::check_match(cx, arms, &self.msrv); @@ -1084,6 +1085,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv); + significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { if self.msrv.meets(msrvs::MATCHES_MACRO) { @@ -1126,8 +1128,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches { ); needless_match::check_if_let(cx, expr, &if_let); } - } else if !from_expansion { - redundant_pattern_match::check(cx, expr); + } else { + if let Some(while_let) = higher::WhileLet::hir(expr) { + significant_drop_in_scrutinee::check_while_let(cx, expr, while_let.let_expr, while_let.if_then); + } + if !from_expansion { + redundant_pattern_match::check(cx, expr); + } } } diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 2f72e59834fa6..9047c9627d9ac 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -16,7 +16,7 @@ use rustc_span::Span; use super::SIGNIFICANT_DROP_IN_SCRUTINEE; -pub(super) fn check<'tcx>( +pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, scrutinee: &'tcx Expr<'_>, @@ -27,10 +27,89 @@ pub(super) fn check<'tcx>( return; } - let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source); + let scrutinee = match (source, &scrutinee.kind) { + (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e, + _ => scrutinee, + }; + + let message = if source == MatchSource::Normal { + "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression" + } else { + "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression" + }; + + let arms = arms.iter().map(|arm| arm.body).collect::>(); + + check(cx, expr, scrutinee, &arms, message, Suggestion::Emit); +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + if_then: &'tcx Expr<'_>, + if_else: Option<&'tcx Expr<'_>>, +) { + if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) { + return; + } + + let message = + "temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression"; + + if let Some(if_else) = if_else { + check(cx, expr, scrutinee, &[if_then, if_else], message, Suggestion::Emit); + } else { + check(cx, expr, scrutinee, &[if_then], message, Suggestion::Emit); + } +} + +pub(super) fn check_while_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, +) { + if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) { + return; + } + + check( + cx, + expr, + scrutinee, + &[body], + "temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression", + // Don't emit wrong suggestions: We cannot fix the significant drop in the `while let` scrutinee by simply + // moving it out. We need to change the `while` to a `loop` instead. + Suggestion::DontEmit, + ); +} + +#[derive(Copy, Clone, Debug)] +enum Suggestion { + Emit, + DontEmit, +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &[&'tcx Expr<'_>], + message: &'static str, + sugg: Suggestion, +) { + let mut helper = SigDropHelper::new(cx); + let suggestions = helper.find_sig_drop(scrutinee); + for found in suggestions { span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { - set_diagnostic(diag, cx, expr, found); + match sugg { + Suggestion::Emit => set_suggestion(diag, cx, expr, found), + Suggestion::DontEmit => (), + } + let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None); diag.span_label(s, "temporary lives until here"); for span in has_significant_drop_in_arms(cx, arms) { @@ -41,7 +120,7 @@ pub(super) fn check<'tcx>( } } -fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { +fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { let original = snippet(cx, found.found_span, ".."); let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); @@ -79,26 +158,6 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: & ); } -/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that -/// may have a surprising lifetime. -fn has_significant_drop_in_scrutinee<'tcx>( - cx: &LateContext<'tcx>, - scrutinee: &'tcx Expr<'tcx>, - source: MatchSource, -) -> (Vec, &'static str) { - let mut helper = SigDropHelper::new(cx); - let scrutinee = match (source, &scrutinee.kind) { - (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e, - _ => scrutinee, - }; - let message = if source == MatchSource::Normal { - "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression" - } else { - "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression" - }; - (helper.find_sig_drop(scrutinee), message) -} - struct SigDropChecker<'a, 'tcx> { seen_types: FxHashSet>, cx: &'a LateContext<'tcx>, @@ -428,10 +487,10 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> { } } -fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet { +fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet { let mut helper = ArmSigDropHelper::new(cx); for arm in arms { - helper.visit_expr(arm.body); + helper.visit_expr(arm); } helper.found_sig_drop_spans } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index e3ce64c246a54..cac2e11f59166 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -167,14 +167,12 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: } else { edits.extend(addr_of_edits); } - edits.push(( - name_span, - String::from(match name { - "map" => "inspect", - "map_err" => "inspect_err", - _ => return, - }), - )); + let edit = match name { + "map" => "inspect", + "map_err" => "inspect_err", + _ => return, + }; + edits.push((name_span, edit.to_string())); edits.push(( final_expr .span @@ -187,9 +185,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: } else { Applicability::MachineApplicable }; - span_lint_and_then(cx, MANUAL_INSPECT, name_span, "", |diag| { - diag.multipart_suggestion("try", edits, app); - }); + span_lint_and_then( + cx, + MANUAL_INSPECT, + name_span, + format!("using `{name}` over `{edit}`"), + |diag| { + diag.multipart_suggestion("try", edits, app); + }, + ); } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1408f45482007..a846552cddfa2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -628,12 +628,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or - /// `_.or_else(|x| Err(y))`. + /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` + /// or `_.or_else(|x| Err(y))`. /// /// ### Why is this bad? - /// Readability, this can be written more concisely as - /// `_.map(|x| y)` or `_.map_err(|x| y)`. + /// This can be written more concisely as `_.map(|x| y)` or `_.map_err(|x| y)`. /// /// ### Example /// ```no_run @@ -4121,7 +4120,7 @@ declare_clippy_lint! { /// ```no_run /// let x = Some(0).inspect(|x| println!("{x}")); /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub MANUAL_INSPECT, complexity, "use of `map` returning the original item" diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index ae9aa83efd685..5d899415d7728 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -1,13 +1,15 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{ get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs, }; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; +use clippy_utils::{ + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty, +}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -52,6 +54,9 @@ pub fn check<'tcx>( if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { return; } + if check_string_from_utf8(cx, expr, receiver) { + return; + } check_other_call_arg(cx, expr, method_name, receiver); } } else { @@ -240,6 +245,65 @@ fn check_into_iter_call_arg( false } +/// Checks for `&String::from_utf8(bytes.{to_vec,to_owned,...}()).unwrap()` coercing to `&str`, +/// which can be written as just `std::str::from_utf8(bytes).unwrap()`. +fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, receiver: &'tcx Expr<'tcx>) -> bool { + if let Some((call, arg)) = skip_addr_of_ancestors(cx, expr) + && !arg.span.from_expansion() + && let ExprKind::Call(callee, _) = call.kind + && fn_def_id(cx, call).is_some_and(|did| match_def_path(cx, did, &paths::STRING_FROM_UTF8)) + && let Some(unwrap_call) = get_parent_expr(cx, call) + && let ExprKind::MethodCall(unwrap_method_name, ..) = unwrap_call.kind + && matches!(unwrap_method_name.ident.name, sym::unwrap | sym::expect) + && let Some(ref_string) = get_parent_expr(cx, unwrap_call) + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = ref_string.kind + && let adjusted_ty = cx.typeck_results().expr_ty_adjusted(ref_string) + // `&...` creates a `&String`, so only actually lint if this coerces to a `&str` + && matches!(adjusted_ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) + { + span_lint_and_then( + cx, + UNNECESSARY_TO_OWNED, + ref_string.span, + "allocating a new `String` only to create a temporary `&str` from it", + |diag| { + let arg_suggestion = format!( + "{borrow}{recv_snippet}", + recv_snippet = snippet(cx, receiver.span.source_callsite(), ".."), + borrow = if cx.typeck_results().expr_ty(receiver).is_ref() { + "" + } else { + // If not already a reference, prefix with a borrow so that it can coerce to one + "&" + } + ); + + diag.multipart_suggestion( + "convert from `&[u8]` to `&str` directly", + vec![ + // `&String::from_utf8(bytes.to_vec()).unwrap()` + // ^^^^^^^^^^^^^^^^^ + (callee.span, "core::str::from_utf8".into()), + // `&String::from_utf8(bytes.to_vec()).unwrap()` + // ^ + ( + ref_string.span.shrink_to_lo().to(unwrap_call.span.shrink_to_lo()), + String::new(), + ), + // `&String::from_utf8(bytes.to_vec()).unwrap()` + // ^^^^^^^^^^^^^^ + (arg.span, arg_suggestion), + ], + Applicability::MachineApplicable, + ); + }, + ); + true + } else { + false + } +} + /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 516b8984ad79d..5b0bd0f716ab2 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; -use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_test, is_lint_allowed}; use rustc_hir::Expr; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; @@ -61,7 +61,7 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { + if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index fca626fa5c35c..c3fbca1d560e6 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; -use std::cmp::Ordering; +use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { /// ### What it does @@ -36,26 +36,21 @@ declare_lint_pass!(MinMaxPass => [MIN_MAX]); impl<'tcx> LateLintPass<'tcx> for MinMaxPass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) { - if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) { - if outer_max == inner_max { - return; - } - match ( - outer_max, - Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c), - ) { - (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (), - _ => { - span_lint( - cx, - MIN_MAX, - expr.span, - "this `min`/`max` combination leads to constant result", - ); - }, - } - } + if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) + && let Some((inner_max, inner_c, ie)) = min_max(cx, oe) + && outer_max != inner_max + && let Some(ord) = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c) + && matches!( + (outer_max, ord), + (MinMax::Max, Equal | Greater) | (MinMax::Min, Equal | Less) + ) + { + span_lint( + cx, + MIN_MAX, + expr.span, + "this `min`/`max` combination leads to constant result", + ); } } } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f3f9bf11a61c4..32c8731c53743 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, - last_path_segment, SpanlessEq, + fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, last_path_segment, + SpanlessEq, }; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || expr.span.desugaring_kind().is_some() - || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + || in_automatically_derived(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index dd98352da8601..935ed48dacc50 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test; use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; -use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { }; // This lint would be very noisy in tests, so just ignore if we're in test context - if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + if is_in_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index bb0d714a31fd4..ed5f46ddc8d20 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -8,9 +8,11 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -115,7 +117,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { .iter() .any(|param| matches!(param.kind, GenericParamKind::Const { .. })); - if already_const(header) || has_const_generic_params { + if already_const(header) + || has_const_generic_params + || !could_be_const_with_abi(cx, &self.msrv, header.abi) + { return; } }, @@ -127,6 +132,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { FnKind::Closure => return, } + if fn_inputs_has_impl_trait_ty(cx, def_id) { + return; + } + let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); // Const fns are not allowed as methods in a trait. @@ -171,3 +180,25 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { fn already_const(header: hir::FnHeader) -> bool { header.constness == Constness::Const } + +fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool { + match abi { + Abi::Rust => true, + // `const extern "C"` was stablized after 1.62.0 + Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN), + // Rest ABIs are still unstable and need the `const_extern_fn` feature enabled. + _ => cx.tcx.features().const_extern_fn, + } +} + +/// Return `true` when the given `def_id` is a function that has `impl Trait` ty as one of +/// its parameter types. +fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + let inputs = cx.tcx.fn_sig(def_id).instantiate_identity().inputs().skip_binder(); + inputs.iter().any(|input| { + matches!( + input.kind(), + ty::Alias(ty::AliasTyKind::Weak, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait() + ) + }) +} diff --git a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/clippy_lints/src/missing_const_for_thread_local.rs similarity index 92% rename from clippy_lints/src/thread_local_initializer_can_be_made_const.rs rename to clippy_lints/src/missing_const_for_thread_local.rs index 4af3ee74d0eaf..ab1b4aa3dee60 100644 --- a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/clippy_lints/src/missing_const_for_thread_local.rs @@ -39,23 +39,23 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.77.0"] - pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, + pub MISSING_CONST_FOR_THREAD_LOCAL, perf, "suggest using `const` in `thread_local!` macro" } -pub struct ThreadLocalInitializerCanBeMadeConst { +pub struct MissingConstForThreadLocal { msrv: Msrv, } -impl ThreadLocalInitializerCanBeMadeConst { +impl MissingConstForThreadLocal { #[must_use] pub fn new(msrv: Msrv) -> Self { Self { msrv } } } -impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]); +impl_lint_pass!(MissingConstForThreadLocal => [MISSING_CONST_FOR_THREAD_LOCAL]); #[inline] fn is_thread_local_initializer( @@ -102,7 +102,7 @@ fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id false } -impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { +impl<'tcx> LateLintPass<'tcx> for MissingConstForThreadLocal { fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { local_defid: rustc_span::def_id::LocalDefId, ) { let defid = local_defid.to_def_id(); - if self.msrv.meets(msrvs::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST) + if self.msrv.meets(msrvs::THREAD_LOCAL_CONST_INIT) && is_thread_local_initializer(cx, fn_kind, span).unwrap_or(false) // Some implementations of `thread_local!` include an initializer fn. // In the case of a const initializer, the init fn is also const, @@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { { span_lint_and_sugg( cx, - THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, + MISSING_CONST_FOR_THREAD_LOCAL, unpeeled.span, "initializer for `thread_local` value can be made `const`", "replace with", diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 57ba0da533190..5ffd41d78e0cb 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// Check if a `&mut` function argument is actually used mutably. /// /// Be careful if the function is publicly reexported as it would break compatibility with - /// users of this function. + /// users of this function, when the users pass this function as an argument. /// /// ### Why is this bad? /// Less `mut` means less fights with the borrow checker. It can also lead to more @@ -138,6 +138,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { return; } + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(fn_def_id) { + return; + } + let hir_id = cx.tcx.local_def_id_to_hir_id(fn_def_id); let is_async = match kind { FnKind::ItemFn(.., header) => { @@ -262,9 +266,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { .iter() .filter(|(def_id, _)| !self.used_fn_def_ids.contains(def_id)) { - let show_semver_warning = - self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(*fn_def_id); - let mut is_cfged = None; for input in unused { // If the argument is never used mutably, we emit the warning. @@ -284,7 +285,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); - if show_semver_warning { + if cx.effective_visibilities.is_exported(*fn_def_id) { diag.warn("changing this function will impact semver compatibility"); } if *is_cfged { diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 87f886b1128d8..0ecfa7baa72d4 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; -use clippy_utils::{any_parent_is_automatically_derived, is_lint_allowed, path_to_local, peel_blocks}; +use clippy_utils::{ + in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, +}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ @@ -185,7 +187,7 @@ impl NoEffect { && has_no_effect(cx, init) && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && ident.name.to_ident_string().starts_with('_') - && !any_parent_is_automatically_derived(cx.tcx, local.hir_id) + && !in_automatically_derived(cx.tcx, local.hir_id) { if let Some(l) = self.local_bindings.last_mut() { l.push(hir_id); @@ -258,13 +260,16 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Semi(expr) = stmt.kind + && !in_external_macro(cx.sess(), stmt.span) && let ctxt = stmt.span.ctxt() && expr.span.ctxt() == ctxt && let Some(reduced) = reduce_expression(cx, expr) - && !in_external_macro(cx.sess(), stmt.span) && reduced.iter().all(|e| e.span.ctxt() == ctxt) { if let ExprKind::Index(..) = &expr.kind { + if is_inside_always_const_context(cx.tcx, expr.hir_id) { + return; + } let snippet = if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { format!("assert!({}.len() > {});", &arr, &func) diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index faab79de9d3cc..0e5b440c50f27 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -57,7 +57,6 @@ pub(crate) fn check<'tcx>( Applicability::HasPlaceholders, // snippet ); } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); } } diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 48a442705b294..59834781a58a5 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -242,6 +242,13 @@ declare_clippy_lint! { /// # let x = 1; /// if (x | 1 > 3) { } /// ``` + /// + /// Use instead: + /// + /// ```no_run + /// # let x = 1; + /// if (x >= 2) { } + /// ``` #[clippy::version = "pre 1.29.0"] pub INEFFECTIVE_BIT_MASK, correctness, @@ -265,6 +272,13 @@ declare_clippy_lint! { /// # let x = 1; /// if x & 0b1111 == 0 { } /// ``` + /// + /// Use instead: + /// + /// ```no_run + /// # let x: i32 = 1; + /// if x.trailing_zeros() > 4 { } + /// ``` #[clippy::version = "pre 1.29.0"] pub VERBOSE_BIT_MASK, pedantic, @@ -560,74 +574,128 @@ declare_clippy_lint! { /// implement equality for a type involving floats). /// /// ### Why is this bad? - /// Floating point calculations are usually imprecise, so - /// asking if two values are *exactly* equal is asking for trouble. For a good - /// guide on what to do, see [the floating point - /// guide](http://www.floating-point-gui.de/errors/comparison). + /// Floating point calculations are usually imprecise, so asking if two values are *exactly* + /// equal is asking for trouble because arriving at the same logical result via different + /// routes (e.g. calculation versus constant) may yield different values. /// /// ### Example + /// /// ```no_run - /// let x = 1.2331f64; - /// let y = 1.2332f64; + /// let a: f64 = 1000.1; + /// let b: f64 = 0.2; + /// let x = a + b; + /// let y = 1000.3; // Expected value. /// - /// if y == 1.23f64 { } - /// if y != x {} // where both are floats + /// // Actual value: 1000.3000000000001 + /// println!("{x}"); + /// + /// let are_equal = x == y; + /// println!("{are_equal}"); // false /// ``` /// - /// Use instead: + /// The correct way to compare floating point numbers is to define an allowed error margin. This + /// may be challenging if there is no "natural" error margin to permit. Broadly speaking, there + /// are two cases: + /// + /// 1. If your values are in a known range and you can define a threshold for "close enough to + /// be equal", it may be appropriate to define an absolute error margin. For example, if your + /// data is "length of vehicle in centimeters", you may consider 0.1 cm to be "close enough". + /// 1. If your code is more general and you do not know the range of values, you should use a + /// relative error margin, accepting e.g. 0.1% of error regardless of specific values. + /// + /// For the scenario where you can define a meaningful absolute error margin, consider using: + /// /// ```no_run - /// # let x = 1.2331f64; - /// # let y = 1.2332f64; - /// let error_margin = f64::EPSILON; // Use an epsilon for comparison - /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error_margin = std::f64::EPSILON; - /// if (y - 1.23f64).abs() < error_margin { } - /// if (y - x).abs() > error_margin { } + /// let a: f64 = 1000.1; + /// let b: f64 = 0.2; + /// let x = a + b; + /// let y = 1000.3; // Expected value. + /// + /// const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1; + /// let within_tolerance = (x - y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM; + /// println!("{within_tolerance}"); // true /// ``` + /// + /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// a different use of the term that is not suitable for floating point equality comparison. + /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. + /// + /// For the scenario where no meaningful absolute error can be defined, refer to + /// [the floating point guide](https://www.floating-point-gui.de/errors/comparison) + /// for a reference implementation of relative error based comparison of floating point values. + /// `MIN_NORMAL` in the reference implementation is equivalent to `MIN_POSITIVE` in Rust. #[clippy::version = "pre 1.29.0"] pub FLOAT_CMP, pedantic, - "using `==` or `!=` on float values instead of comparing difference with an epsilon" + "using `==` or `!=` on float values instead of comparing difference with an allowed error" } declare_clippy_lint! { /// ### What it does - /// Checks for (in-)equality comparisons on floating-point - /// value and constant, except in functions called `*eq*` (which probably + /// Checks for (in-)equality comparisons on constant floating-point + /// values (apart from zero), except in functions called `*eq*` (which probably /// implement equality for a type involving floats). /// /// ### Why restrict this? - /// Floating point calculations are usually imprecise, so - /// asking if two values are *exactly* equal is asking for trouble. For a good - /// guide on what to do, see [the floating point - /// guide](http://www.floating-point-gui.de/errors/comparison). + /// Floating point calculations are usually imprecise, so asking if two values are *exactly* + /// equal is asking for trouble because arriving at the same logical result via different + /// routes (e.g. calculation versus constant) may yield different values. /// /// ### Example + /// /// ```no_run - /// let x: f64 = 1.0; - /// const ONE: f64 = 1.00; + /// let a: f64 = 1000.1; + /// let b: f64 = 0.2; + /// let x = a + b; + /// const Y: f64 = 1000.3; // Expected value. + /// + /// // Actual value: 1000.3000000000001 + /// println!("{x}"); /// - /// if x == ONE { } // where both are floats + /// let are_equal = x == Y; + /// println!("{are_equal}"); // false /// ``` /// - /// Use instead: + /// The correct way to compare floating point numbers is to define an allowed error margin. This + /// may be challenging if there is no "natural" error margin to permit. Broadly speaking, there + /// are two cases: + /// + /// 1. If your values are in a known range and you can define a threshold for "close enough to + /// be equal", it may be appropriate to define an absolute error margin. For example, if your + /// data is "length of vehicle in centimeters", you may consider 0.1 cm to be "close enough". + /// 1. If your code is more general and you do not know the range of values, you should use a + /// relative error margin, accepting e.g. 0.1% of error regardless of specific values. + /// + /// For the scenario where you can define a meaningful absolute error margin, consider using: + /// /// ```no_run - /// # let x: f64 = 1.0; - /// # const ONE: f64 = 1.00; - /// let error_margin = f64::EPSILON; // Use an epsilon for comparison - /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error_margin = std::f64::EPSILON; - /// if (x - ONE).abs() < error_margin { } + /// let a: f64 = 1000.1; + /// let b: f64 = 0.2; + /// let x = a + b; + /// const Y: f64 = 1000.3; // Expected value. + /// + /// const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1; + /// let within_tolerance = (x - Y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM; + /// println!("{within_tolerance}"); // true /// ``` + /// + /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// a different use of the term that is not suitable for floating point equality comparison. + /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. + /// + /// For the scenario where no meaningful absolute error can be defined, refer to + /// [the floating point guide](https://www.floating-point-gui.de/errors/comparison) + /// for a reference implementation of relative error based comparison of floating point values. + /// `MIN_NORMAL` in the reference implementation is equivalent to `MIN_POSITIVE` in Rust. #[clippy::version = "pre 1.29.0"] pub FLOAT_CMP_CONST, restriction, - "using `==` or `!=` on float constants instead of comparing difference with an epsilon" + "using `==` or `!=` on float constants instead of comparing difference with an allowed error" } declare_clippy_lint! { /// ### What it does - /// Checks for getting the remainder of a division by one or minus + /// Checks for getting the remainder of integer division by one or minus /// one. /// /// ### Why is this bad? @@ -646,7 +714,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub MODULO_ONE, correctness, - "taking a number modulo +/-1, which can either panic/overflow or always returns 0" + "taking an integer modulo +/-1, which can either panic/overflow or always returns 0" } declare_clippy_lint! { diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs deleted file mode 100644 index de78987933108..0000000000000 --- a/clippy_lints/src/overflow_check_conditional.rs +++ /dev/null @@ -1,70 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::SpanlessEq; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; - -declare_clippy_lint! { - /// ### What it does - /// Detects classic underflow/overflow checks. - /// - /// ### Why is this bad? - /// Most classic C underflow/overflow checks will fail in - /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead. - /// - /// ### Example - /// ```no_run - /// # let a = 1; - /// # let b = 2; - /// a + b < a; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub OVERFLOW_CHECK_CONDITIONAL, - complexity, - "overflow checks inspired by C which are likely to panic" -} - -declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]); - -const OVERFLOW_MSG: &str = "you are trying to use classic C overflow conditions that will fail in Rust"; -const UNDERFLOW_MSG: &str = "you are trying to use classic C underflow conditions that will fail in Rust"; - -impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { - // a + b < a, a > a + b, a < a - b, a - b > a - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); - if let ExprKind::Binary(ref op, first, second) = expr.kind - && let ExprKind::Binary(ref op2, ident1, ident2) = first.kind - && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind - && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind - && let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind - && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) - && cx.typeck_results().expr_ty(ident1).is_integral() - && cx.typeck_results().expr_ty(ident2).is_integral() - { - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } - } - - if let ExprKind::Binary(ref op, first, second) = expr.kind - && let ExprKind::Binary(ref op2, ident1, ident2) = second.kind - && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind - && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind - && let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind - && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) - && cx.typeck_results().expr_ty(ident1).is_integral() - && cx.typeck_results().expr_ty(ident2).is_integral() - { - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } - } - } -} diff --git a/clippy_lints/src/panicking_overflow_checks.rs b/clippy_lints/src/panicking_overflow_checks.rs new file mode 100644 index 0000000000000..7f100a746d5ed --- /dev/null +++ b/clippy_lints/src/panicking_overflow_checks.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::eq_expr_value; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Detects C-style underflow/overflow checks. + /// + /// ### Why is this bad? + /// These checks will, by default, panic in debug builds rather than check + /// whether the operation caused an overflow. + /// + /// ### Example + /// ```no_run + /// # let a = 1i32; + /// # let b = 2i32; + /// if a + b < a { + /// // handle overflow + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// # let a = 1i32; + /// # let b = 2i32; + /// if a.checked_add(b).is_none() { + /// // handle overflow + /// } + /// ``` + /// + /// Or: + /// ```no_run + /// # let a = 1i32; + /// # let b = 2i32; + /// if a.overflowing_add(b).1 { + /// // handle overflow + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PANICKING_OVERFLOW_CHECKS, + correctness, + "overflow checks which will panic in debug mode" +} + +declare_lint_pass!(PanickingOverflowChecks => [PANICKING_OVERFLOW_CHECKS]); + +impl<'tcx> LateLintPass<'tcx> for PanickingOverflowChecks { + // a + b < a, a > a + b, a < a - b, a - b > a + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(op, lhs, rhs) = expr.kind + && let (lt, gt) = match op.node { + BinOpKind::Lt => (lhs, rhs), + BinOpKind::Gt => (rhs, lhs), + _ => return, + } + && let ctxt = expr.span.ctxt() + && let (op_lhs, op_rhs, other, commutative) = match (<.kind, >.kind) { + (&ExprKind::Binary(op, lhs, rhs), _) if op.node == BinOpKind::Add && ctxt == lt.span.ctxt() => { + (lhs, rhs, gt, true) + }, + (_, &ExprKind::Binary(op, lhs, rhs)) if op.node == BinOpKind::Sub && ctxt == gt.span.ctxt() => { + (lhs, rhs, lt, false) + }, + _ => return, + } + && let typeck = cx.typeck_results() + && let ty = typeck.expr_ty(op_lhs) + && matches!(ty.kind(), ty::Uint(_)) + && ty == typeck.expr_ty(op_rhs) + && ty == typeck.expr_ty(other) + && !in_external_macro(cx.tcx.sess, expr.span) + && (eq_expr_value(cx, op_lhs, other) || (commutative && eq_expr_value(cx, op_rhs, other))) + { + span_lint( + cx, + PANICKING_OVERFLOW_CHECKS, + expr.span, + "you are trying to use classic C overflow conditions that will fail in Rust", + ); + } + } +} diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 85979903b587a..8e999f3e89af3 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -26,12 +26,14 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), ("clippy::option_unwrap_used", "clippy::unwrap_used"), + ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), ("clippy::ref_in_deref", "clippy::needless_borrow"), ("clippy::result_expect_used", "clippy::expect_used"), ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), ("clippy::result_unwrap_used", "clippy::unwrap_used"), ("clippy::single_char_push_str", "clippy::single_char_add_str"), ("clippy::stutter", "clippy::module_name_repetitions"), + ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), ("clippy::zero_width_space", "clippy::invisible_characters"), diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index c11da3147ef4a..8ced47b48a43d 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -7,6 +7,7 @@ use clippy_utils::{ path_to_local_id, span_contains_cfg, span_find_starting_semi, }; use core::ops::ControlFlow; +use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::ResultErr; @@ -14,13 +15,13 @@ use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, StmtKind, }; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{sym, BytePos, Pos, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -80,6 +81,9 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_RETURN, + // This lint requires some special handling in `check_final_expr` for `#[expect]`. + // This handling needs to be updated if the group gets changed. This should also + // be caught by tests. style, "using a return statement like `return expr;` where an expression would suffice" } @@ -91,6 +95,9 @@ declare_clippy_lint! { /// ### Why is this bad? /// The `return` is unnecessary. /// + /// Returns may be used to add attributes to the return expression. Return + /// statements with attributes are therefore be accepted by this lint. + /// /// ### Example /// ```rust,ignore /// fn foo(x: usize) -> Result<(), Box> { @@ -377,13 +384,39 @@ fn check_final_expr<'tcx>( } }; - if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { - return; - } let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if borrows { return; } + if ret_span.from_expansion() { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // `#[expect(clippy::needless_return)]` needs to be handled separatly to + // actually fullfil the expectation (clippy::#12998) + match cx.tcx.hir().attrs(expr.hir_id) { + [] => {}, + [attr] => { + if matches!(Level::from_attr(attr), Some(Level::Expect(_))) + && let metas = attr.meta_item_list() + && let Some(lst) = metas + && let [NestedMetaItem::MetaItem(meta_item)] = lst.as_slice() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && matches!( + lint_name.ident.name.as_str(), + "needless_return" | "style" | "all" | "warnings" + ) + { + // This is an expectation of the `needless_return` lint + } else { + return; + } + }, + _ => return, + } emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); }, @@ -415,10 +448,6 @@ fn emit_return_lint( replacement: &RetReplacement<'_>, at: HirId, ) { - if ret_span.from_expansion() { - return; - } - span_lint_hir_and_then( cx, NEEDLESS_RETURN, diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 8fdd19c549f5a..508f3ae6def02 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -12,7 +12,7 @@ use std::collections::{BTreeMap, BTreeSet}; declare_clippy_lint! { /// ### What it does /// It lints if a struct has two methods with the same name: - /// one from a trait, another not from trait. + /// one from a trait, another not from a trait. /// /// ### Why restrict this? /// Confusing. diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs new file mode 100644 index 0000000000000..5e65b9fa5171d --- /dev/null +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -0,0 +1,125 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{higher, peel_hir_expr_while, SpanlessEq}; +use rustc_hir::{Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `contains` to see if a value is not + /// present on `HashSet` followed by a `insert`. + /// + /// ### Why is this bad? + /// Using just `insert` and checking the returned `bool` is more efficient. + /// + /// ### Known problems + /// In case the value that wants to be inserted is borrowed and also expensive or impossible + /// to clone. In such a scenario, the developer might want to check with `contains` before inserting, + /// to avoid the clone. In this case, it will report a false positive. + /// + /// ### Example + /// ```rust + /// use std::collections::HashSet; + /// let mut set = HashSet::new(); + /// let value = 5; + /// if !set.contains(&value) { + /// set.insert(value); + /// println!("inserted {value:?}"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::collections::HashSet; + /// let mut set = HashSet::new(); + /// let value = 5; + /// if set.insert(&value) { + /// println!("inserted {value:?}"); + /// } + /// ``` + #[clippy::version = "1.80.0"] + pub SET_CONTAINS_OR_INSERT, + nursery, + "call to `HashSet::contains` followed by `HashSet::insert`" +} + +declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]); + +impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() + && let Some(higher::If { + cond: cond_expr, + then: then_expr, + .. + }) = higher::If::hir(expr) + && let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) + { + span_lint( + cx, + SET_CONTAINS_OR_INSERT, + vec![contains_expr.span, insert_expr.span], + "usage of `HashSet::insert` after `HashSet::contains`", + ); + } + } +} + +struct OpExpr<'tcx> { + receiver: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, + span: Span, +} + +fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option> { + let expr = peel_hir_expr_while(expr, |e| { + if let ExprKind::Unary(UnOp::Not, e) = e.kind { + Some(e) + } else { + None + } + }); + + if let ExprKind::MethodCall(path, receiver, [value], span) = expr.kind { + let value = value.peel_borrows(); + let value = peel_hir_expr_while(value, |e| { + if let ExprKind::Unary(UnOp::Deref, e) = e.kind { + Some(e) + } else { + None + } + }); + let receiver = receiver.peel_borrows(); + let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); + if value.span.eq_ctxt(expr.span) + && is_type_diagnostic_item(cx, receiver_ty, sym::HashSet) + && path.ident.name == symbol + { + return Some(OpExpr { receiver, value, span }); + } + } + None +} + +fn find_insert_calls<'tcx>( + cx: &LateContext<'tcx>, + contains_expr: &OpExpr<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> { + for_each_expr(cx, expr, |e| { + if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert)) + && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) + && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) + { + ControlFlow::Break(insert_expr) + } else { + ControlFlow::Continue(()) + } + }) +} diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 877a77fd6d242..1d294c2944b2c 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,5 @@ pub mod almost_standard_lint_formulation; pub mod collapsible_calls; -pub mod compiler_lint_functions; pub mod interning_defined_symbol; pub mod invalid_paths; pub mod lint_without_lint_pass; diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs deleted file mode 100644 index 9b6b687181867..0000000000000 --- a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ /dev/null @@ -1,73 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::match_type; -use clippy_utils::{is_lint_allowed, paths}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` - /// variant of the function. - /// - /// ### Why is this bad? - /// The `utils::*` variants also add a link to the Clippy documentation to the - /// warning/error messages. - /// - /// ### Example - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::span_lint(cx, LINT_NAME, "message"); - /// ``` - pub COMPILER_LINT_FUNCTIONS, - internal, - "usage of the lint functions of the compiler instead of the utils::* variant" -} - -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -#[derive(Clone, Default)] -pub struct CompilerLintFunctions { - map: FxHashMap<&'static str, &'static str>, -} - -impl CompilerLintFunctions { - #[must_use] - pub fn new() -> Self { - let mut map = FxHashMap::default(); - map.insert("span_lint", "utils::span_lint"); - map.insert("lint", "utils::span_lint"); - map.insert("span_lint_note", "utils::span_lint_and_note"); - map.insert("span_lint_help", "utils::span_lint_and_help"); - Self { map } - } -} - -impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { - return; - } - - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind - && let fn_name = path.ident - && let Some(sugg) = self.map.get(fn_name.as_str()) - && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && (match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT)) - { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } - } -} diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 436f0cb79fb26..a0a60ca887583 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_test_module_or_function; +use clippy_utils::is_in_test; use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -100,7 +100,6 @@ declare_clippy_lint! { #[derive(Default)] pub struct WildcardImports { warn_on_all: bool, - test_modules_deep: u32, allowed_segments: FxHashSet, } @@ -108,7 +107,6 @@ impl WildcardImports { pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet) -> Self { Self { warn_on_all, - test_modules_deep: 0, allowed_segments: allowed_wildcard_imports, } } @@ -122,15 +120,12 @@ impl LateLintPass<'_> for WildcardImports { return; } - if is_test_module_or_function(cx.tcx, item) { - self.test_modules_deep = self.test_modules_deep.saturating_add(1); - } let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id); if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { return; } if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind - && (self.warn_on_all || !self.check_exceptions(item, use_path.segments)) + && (self.warn_on_all || !self.check_exceptions(cx, item, use_path.segments)) && let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id) && !used_imports.is_empty() // Already handled by `unused_imports` && !used_imports.contains(&kw::Underscore) @@ -180,20 +175,14 @@ impl LateLintPass<'_> for WildcardImports { span_lint_and_sugg(cx, lint, span, message, "try", sugg, applicability); } } - - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(cx.tcx, item) { - self.test_modules_deep = self.test_modules_deep.saturating_sub(1); - } - } } impl WildcardImports { - fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { + fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { item.span.from_expansion() || is_prelude_import(segments) - || (is_super_only_import(segments) && self.test_modules_deep > 0) || is_allowed_via_config(segments, &self.allowed_segments) + || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 652ce88bd9516..96e53b7ef0ba7 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::is_in_test; use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; -use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -297,8 +297,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { .as_ref() .map_or(false, |crate_name| crate_name == "build_script_build"); - let allowed_in_tests = self.allow_print_in_tests - && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)); + let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id); match diag_name { sym::print_macro | sym::println_macro if !allowed_in_tests => { if !is_build_script { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 935a25d79319d..bdb3b5e45c48d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -102,10 +102,11 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr, - ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, - ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, - PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, + self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstContext, + Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, + ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, + TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -210,7 +211,10 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { false } -/// Returns `true` if the given `NodeId` is inside a constant context +/// Returns `true` if the given `HirId` is inside a constant context. +/// +/// This is the same as `is_inside_always_const_context`, but also includes +/// `const fn`. /// /// # Example /// @@ -223,6 +227,24 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx.hir().is_inside_const_context(id) } +/// Returns `true` if the given `HirId` is inside an always constant context. +/// +/// This context includes: +/// * const/static items +/// * const blocks (or inline consts) +/// * associated constants +pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { + use ConstContext::{Const, ConstFn, Static}; + let hir = tcx.hir(); + let Some(ctx) = hir.body_const_context(hir.enclosing_body_owner(hir_id)) else { + return false; + }; + match ctx { + ConstFn => false, + Static(_) | Const { inline: _ } => true, + } +} + /// Checks if a `Res` refers to a constructor of a `LangItem` /// For example, use this to check whether a function call or a pattern is `Some(..)`. pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { @@ -1904,8 +1926,18 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool false } -pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool { - any_parent_has_attr(tcx, node, sym::automatically_derived) +/// Checks if the given HIR node is inside an `impl` block with the `automatically_derived` +/// attribute. +pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { + tcx.hir() + .parent_owner_iter(id) + .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_)))) + .any(|(id, _)| { + has_attr( + tcx.hir().attrs(tcx.local_def_id_to_hir_id(id.def_id)), + sym::automatically_derived, + ) + }) } /// Matches a function call with the given path and returns the arguments. @@ -2472,6 +2504,17 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) } } +/// Peels off all references on the type. Returns the underlying type and the number of references +/// removed. +pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { + let mut count = 0; + while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() { + ty = *dest_ty; + count += 1; + } + (ty, count) +} + /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed. pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { @@ -2594,16 +2637,6 @@ pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { .any(|attr| attr.has_name(sym::cfg)) } -/// Checks whether item either has `test` attribute applied, or -/// is a module with `test` in its name. -/// -/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function -pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { - is_in_test_function(tcx, item.hir_id()) - || matches!(item.kind, ItemKind::Mod(..)) - && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") -} - /// Walks up the HIR tree from the given expression in an attempt to find where the value is /// consumed. /// diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 3f66813801dc0..d5a3d8b9e5a2e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -88,6 +88,7 @@ pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern" pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +pub const STRING_FROM_UTF8: [&str; 4] = ["alloc", "string", "String", "from_utf8"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index acaeb93f44a6e..fc02b974ee12a 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -96,11 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx - .tcx - .explicit_item_super_predicates(def_id) - .iter_identity_copied() - { + for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. @@ -1332,19 +1328,13 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { - cx.tcx - .inherent_impls(ty_did) - .into_iter() - .flatten() - .map(|&did| { - cx.tcx - .associated_items(did) - .filter_by_name_unhygienic(method_name) - .next() - .filter(|item| item.kind == AssocKind::Fn) - }) - .next() - .flatten() + cx.tcx.inherent_impls(ty_did).into_iter().flatten().find_map(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .filter(|item| item.kind == AssocKind::Fn) + }) } else { None } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index ae9e77b8eed05..3c86dfe324f4d 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -16,6 +16,7 @@ clap = { version = "4.4", features = ["derive", "env"] } crossbeam-channel = "0.5.6" diff = "0.1.13" flate2 = "1.0" +itertools = "0.12" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" diff --git a/lintcheck/README.md b/lintcheck/README.md index 2d6039caeef07..47a96e0a03c9c 100644 --- a/lintcheck/README.md +++ b/lintcheck/README.md @@ -7,13 +7,13 @@ repo. We can then check the diff and spot new or disappearing warnings. From the repo root, run: ``` -cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml +cargo lintcheck ``` or ``` -cargo lintcheck +cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml ``` By default, the logs will be saved into @@ -33,6 +33,8 @@ the 200 recently most downloaded crates: cargo lintcheck popular -n 200 custom.toml ``` +> Note: Lintcheck isn't sandboxed. Only use it to check crates that you trust or +> sandbox it manually. ### Configuring the Crate Sources @@ -65,17 +67,11 @@ sources. #### Command Line Options (optional) ```toml -bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']} +clap = {name = "clap", versions = ['4.5.8'], options = ['-Fderive']} ``` It is possible to specify command line options for each crate. This makes it -possible to only check a crate for certain lint groups. If no options are -specified, the lint groups `clippy::all`, `clippy::pedantic`, and -`clippy::cargo` are checked. If an empty array is specified only `clippy::all` -is checked. - -**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all` -is explicitly specified in the options. +possible to enable or disable features. ### Fix mode You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index 52f7fee47b616..ff608e6f93592 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -1,38 +1,38 @@ [crates] # some of these are from cargotest -cargo = {name = "cargo", versions = ['0.64.0']} -iron = {name = "iron", versions = ['0.6.1']} -ripgrep = {name = "ripgrep", versions = ['12.1.1']} -xsv = {name = "xsv", versions = ['0.13.0']} +cargo = {name = "cargo", version = '0.64.0'} +iron = {name = "iron", version = '0.6.1'} +ripgrep = {name = "ripgrep", version = '12.1.1'} +xsv = {name = "xsv", version = '0.13.0'} # commented out because of 173K clippy::match_same_arms msgs in language_type.rs -#tokei = { name = "tokei", versions = ['12.0.4']} -rayon = {name = "rayon", versions = ['1.5.0']} -serde = {name = "serde", versions = ['1.0.118']} +#tokei = { name = "tokei", version = '12.0.4'} +rayon = {name = "rayon", version = '1.5.0'} +serde = {name = "serde", version = '1.0.118'} # top 10 crates.io dls -bitflags = {name = "bitflags", versions = ['1.2.1']} +bitflags = {name = "bitflags", version = '1.2.1'} # crash = {name = "clippy_crash", path = "/tmp/clippy_crash"} -libc = {name = "libc", versions = ['0.2.81']} -log = {name = "log", versions = ['0.4.11']} -proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']} -quote = {name = "quote", versions = ['1.0.7']} -rand = {name = "rand", versions = ['0.7.3']} -rand_core = {name = "rand_core", versions = ['0.6.0']} -regex = {name = "regex", versions = ['1.3.2']} -syn = {name = "syn", versions = ['1.0.54']} -unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} +libc = {name = "libc", version = '0.2.81'} +log = {name = "log", version = '0.4.11'} +proc-macro2 = {name = "proc-macro2", version = '1.0.24'} +quote = {name = "quote", version = '1.0.7'} +rand = {name = "rand", version = '0.7.3'} +rand_core = {name = "rand_core", version = '0.6.0'} +regex = {name = "regex", version = '1.3.2'} +syn = {name = "syn", version = '1.0.54'} +unicode-xid = {name = "unicode-xid", version = '0.2.1'} # some more of dtolnays crates -anyhow = {name = "anyhow", versions = ['1.0.38']} -async-trait = {name = "async-trait", versions = ['0.1.42']} -cxx = {name = "cxx", versions = ['1.0.32']} -ryu = {name = "ryu", versions = ['1.0.5']} -serde_yaml = {name = "serde_yaml", versions = ['0.8.17']} -thiserror = {name = "thiserror", versions = ['1.0.24']} +anyhow = {name = "anyhow", version = '1.0.38'} +async-trait = {name = "async-trait", version = '0.1.42'} +cxx = {name = "cxx", version = '1.0.32'} +ryu = {name = "ryu", version = '1.0.5'} +serde_yaml = {name = "serde_yaml", version = '0.8.17'} +thiserror = {name = "thiserror", version = '1.0.24'} # some embark crates, there are other interesting crates but # unfortunately adding them increases lintcheck runtime drastically -cfg-expr = {name = "cfg-expr", versions = ['0.7.1']} +cfg-expr = {name = "cfg-expr", version = '0.7.1'} puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} -rpmalloc = {name = "rpmalloc", versions = ['0.2.0']} -tame-oidc = {name = "tame-oidc", versions = ['0.1.0']} +rpmalloc = {name = "rpmalloc", version = '0.2.0'} +tame-oidc = {name = "tame-oidc", version = '0.1.0'} [recursive] ignore = [ diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index e6cd7c9fdc2bf..b35a62eed4405 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -36,6 +36,10 @@ pub(crate) struct LintcheckConfig { /// Apply a filter to only collect specified lints, this also overrides `allow` attributes #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)] pub lint_filter: Vec, + /// Set all lints to the "warn" lint level, even resitriction ones. Usually, + /// it's better to use `--filter` instead + #[clap(long, conflicts_with("lint_filter"))] + pub warn_all: bool, /// Set the output format of the log file #[clap(long, short, default_value = "text")] pub format: OutputFormat, diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 47724a2fedb00..041be5081f28c 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -11,8 +11,6 @@ use std::{env, mem}; fn run_clippy(addr: &str) -> Option { let driver_info = DriverInfo { package_name: env::var("CARGO_PKG_NAME").ok()?, - crate_name: env::var("CARGO_CRATE_NAME").ok()?, - version: env::var("CARGO_PKG_VERSION").ok()?, }; let mut stream = BufReader::new(TcpStream::connect(addr).unwrap()); diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs new file mode 100644 index 0000000000000..3d034391c280f --- /dev/null +++ b/lintcheck/src/input.rs @@ -0,0 +1,288 @@ +use std::collections::{HashMap, HashSet}; +use std::fs::{self}; +use std::io::{self, ErrorKind}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::time::Duration; + +use serde::Deserialize; +use walkdir::{DirEntry, WalkDir}; + +use crate::{Crate, LINTCHECK_DOWNLOADS, LINTCHECK_SOURCES}; + +/// List of sources to check, loaded from a .toml file +#[derive(Debug, Deserialize)] +pub struct SourceList { + crates: HashMap, + #[serde(default)] + recursive: RecursiveOptions, +} + +#[derive(Debug, Deserialize, Default)] +pub struct RecursiveOptions { + pub ignore: HashSet, +} + +/// A crate source stored inside the .toml +/// will be translated into on one of the `CrateSource` variants +#[derive(Debug, Deserialize)] +struct TomlCrate { + name: String, + version: Option, + git_url: Option, + git_hash: Option, + path: Option, + options: Option>, +} + +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub enum CrateSource { + CratesIo { + name: String, + version: String, + options: Option>, + }, + Git { + name: String, + url: String, + commit: String, + options: Option>, + }, + Path { + name: String, + path: PathBuf, + options: Option>, + }, +} + +/// Read a `lintcheck_crates.toml` file +pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { + let toml_content: String = + fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + let crate_list: SourceList = + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); + // parse the hashmap of the toml file into a list of crates + let tomlcrates: Vec = crate_list.crates.into_values().collect(); + + // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => + // multiple Cratesources) + let mut crate_sources = Vec::new(); + for tk in tomlcrates { + if let Some(ref path) = tk.path { + crate_sources.push(CrateSource::Path { + name: tk.name.clone(), + path: PathBuf::from(path), + options: tk.options.clone(), + }); + } else if let Some(ref version) = tk.version { + crate_sources.push(CrateSource::CratesIo { + name: tk.name.clone(), + version: version.to_string(), + options: tk.options.clone(), + }); + } else if tk.git_url.is_some() && tk.git_hash.is_some() { + // otherwise, we should have a git source + crate_sources.push(CrateSource::Git { + name: tk.name.clone(), + url: tk.git_url.clone().unwrap(), + commit: tk.git_hash.clone().unwrap(), + options: tk.options.clone(), + }); + } else { + panic!("Invalid crate source: {tk:?}"); + } + + // if we have a version as well as a git data OR only one git data, something is funky + if tk.version.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) + || tk.git_hash.is_some() != tk.git_url.is_some() + { + eprintln!("tomlkrate: {tk:?}"); + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.version.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); + unreachable!("Failed to translate TomlCrate into CrateSource!"); + } + } + // sort the crates + crate_sources.sort(); + + (crate_sources, crate_list.recursive) +} + +impl CrateSource { + /// Makes the sources available on the disk for clippy to check. + /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or + /// copies a local folder + #[expect(clippy::too_many_lines)] + pub fn download_and_extract(&self) -> Crate { + #[allow(clippy::result_large_err)] + fn get(path: &str) -> Result { + const MAX_RETRIES: u8 = 4; + let mut retries = 0; + loop { + match ureq::get(path).call() { + Ok(res) => return Ok(res), + Err(e) if retries >= MAX_RETRIES => return Err(e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), + Err(e) => return Err(e), + } + eprintln!("retrying in {retries} seconds..."); + std::thread::sleep(Duration::from_secs(u64::from(retries))); + retries += 1; + } + } + match self { + CrateSource::CratesIo { name, version, options } => { + let extract_dir = PathBuf::from(LINTCHECK_SOURCES); + let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); + + // url to download the crate from crates.io + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); + create_dirs(&krate_download_dir, &extract_dir); + + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); + // don't download/extract if we already have done so + if !krate_file_path.is_file() { + // create a file path to download and write the crate data into + let mut krate_dest = fs::File::create(&krate_file_path).unwrap(); + let mut krate_req = get(&url).unwrap().into_reader(); + // copy the crate into the file + io::copy(&mut krate_req, &mut krate_dest).unwrap(); + + // unzip the tarball + let ungz_tar = flate2::read::GzDecoder::new(fs::File::open(&krate_file_path).unwrap()); + // extract the tar archive + let mut archive = tar::Archive::new(ungz_tar); + archive.unpack(&extract_dir).expect("Failed to extract!"); + } + // crate is extracted, return a new Krate object which contains the path to the extracted + // sources that clippy can check + Crate { + version: version.clone(), + name: name.clone(), + path: extract_dir.join(format!("{name}-{version}/")), + options: options.clone(), + } + }, + CrateSource::Git { + name, + url, + commit, + options, + } => { + let repo_path = { + let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); + // add a -git suffix in case we have the same crate from crates.io and a git repo + repo_path.push(format!("{name}-git")); + repo_path + }; + // clone the repo if we have not done so + if !repo_path.is_dir() { + println!("Cloning {url} and checking out {commit}"); + if !Command::new("git") + .arg("clone") + .arg(url) + .arg(&repo_path) + .status() + .expect("Failed to clone git repo!") + .success() + { + eprintln!("Failed to clone {url} into {}", repo_path.display()); + } + } + // check out the commit/branch/whatever + if !Command::new("git") + .args(["-c", "advice.detachedHead=false"]) + .arg("checkout") + .arg(commit) + .current_dir(&repo_path) + .status() + .expect("Failed to check out commit") + .success() + { + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); + } + + Crate { + version: commit.clone(), + name: name.clone(), + path: repo_path, + options: options.clone(), + } + }, + CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. + // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory + // as a result of this filter. + let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); + if dest_crate_root.exists() { + println!("Deleting existing directory at {dest_crate_root:?}"); + fs::remove_dir_all(&dest_crate_root).unwrap(); + } + + println!("Copying {path:?} to {dest_crate_root:?}"); + + for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { + let entry = entry.unwrap(); + let entry_path = entry.path(); + let relative_entry_path = entry_path.strip_prefix(path).unwrap(); + let dest_path = dest_crate_root.join(relative_entry_path); + let metadata = entry_path.symlink_metadata().unwrap(); + + if metadata.is_dir() { + fs::create_dir(dest_path).unwrap(); + } else if metadata.is_file() { + fs::copy(entry_path, dest_path).unwrap(); + } + } + + Crate { + version: String::from("local"), + name: name.clone(), + path: dest_crate_root, + options: options.clone(), + } + }, + } + } +} + +/// Create necessary directories to run the lintcheck tool. +/// +/// # Panics +/// +/// This function panics if creating one of the dirs fails. +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { + fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); + }); + fs::create_dir(krate_download_dir).unwrap_or_else(|err| { + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); + }); + fs::create_dir(extract_dir).unwrap_or_else(|err| { + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); + }); +} diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 43d0413c7ceeb..1a652927988e1 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -1,37 +1,50 @@ -use std::collections::HashMap; -use std::fmt::Write; use std::fs; -use std::hash::Hash; use std::path::Path; +use itertools::EitherOrBoth; +use serde::{Deserialize, Serialize}; + use crate::ClippyWarning; -/// Creates the log file output for [`crate::config::OutputFormat::Json`] -pub(crate) fn output(clippy_warnings: &[ClippyWarning]) -> String { - serde_json::to_string(&clippy_warnings).unwrap() +#[derive(Deserialize, Serialize)] +struct LintJson { + lint: String, + file_name: String, + byte_pos: (u32, u32), + rendered: String, } -fn load_warnings(path: &Path) -> Vec { - let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display())); - - serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) +impl LintJson { + fn key(&self) -> impl Ord + '_ { + (self.file_name.as_str(), self.byte_pos, self.lint.as_str()) + } } -/// Group warnings by their primary span location + lint name -fn create_map(warnings: &[ClippyWarning]) -> HashMap> { - let mut map = HashMap::<_, Vec<_>>::with_capacity(warnings.len()); - - for warning in warnings { - let span = warning.span(); - let key = (&warning.lint_type, &span.file_name, span.byte_start, span.byte_end); +/// Creates the log file output for [`crate::config::OutputFormat::Json`] +pub(crate) fn output(clippy_warnings: Vec) -> String { + let mut lints: Vec = clippy_warnings + .into_iter() + .map(|warning| { + let span = warning.span(); + LintJson { + file_name: span.file_name.clone(), + byte_pos: (span.byte_start, span.byte_end), + lint: warning.lint, + rendered: warning.diag.rendered.unwrap(), + } + }) + .collect(); + lints.sort_by(|a, b| a.key().cmp(&b.key())); + serde_json::to_string(&lints).unwrap() +} - map.entry(key).or_default().push(warning); - } +fn load_warnings(path: &Path) -> Vec { + let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display())); - map + serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) } -fn print_warnings(title: &str, warnings: &[&ClippyWarning]) { +fn print_warnings(title: &str, warnings: &[LintJson]) { if warnings.is_empty() { return; } @@ -39,31 +52,20 @@ fn print_warnings(title: &str, warnings: &[&ClippyWarning]) { println!("### {title}"); println!("```"); for warning in warnings { - print!("{}", warning.diag); + print!("{}", warning.rendered); } println!("```"); } -fn print_changed_diff(changed: &[(&[&ClippyWarning], &[&ClippyWarning])]) { - fn render(warnings: &[&ClippyWarning]) -> String { - let mut rendered = String::new(); - for warning in warnings { - write!(&mut rendered, "{}", warning.diag).unwrap(); - } - rendered - } - +fn print_changed_diff(changed: &[(LintJson, LintJson)]) { if changed.is_empty() { return; } println!("### Changed"); println!("```diff"); - for &(old, new) in changed { - let old_rendered = render(old); - let new_rendered = render(new); - - for change in diff::lines(&old_rendered, &new_rendered) { + for (old, new) in changed { + for change in diff::lines(&old.rendered, &new.rendered) { use diff::Result::{Both, Left, Right}; match change { @@ -86,26 +88,19 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); - let old_map = create_map(&old_warnings); - let new_map = create_map(&new_warnings); - let mut added = Vec::new(); let mut removed = Vec::new(); let mut changed = Vec::new(); - for (key, new) in &new_map { - if let Some(old) = old_map.get(key) { - if old != new { - changed.push((old.as_slice(), new.as_slice())); - } - } else { - added.extend(new); - } - } - - for (key, old) in &old_map { - if !new_map.contains_key(key) { - removed.extend(old); + for change in itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) { + match change { + EitherOrBoth::Both(old, new) => { + if old.rendered != new.rendered { + changed.push((old, new)); + } + }, + EitherOrBoth::Left(old) => removed.push(old), + EitherOrBoth::Right(new) => added.push(new), } } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index ec72e0eb5dcb5..e37ffab13ac89 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -5,6 +5,7 @@ // When a new lint is introduced, we can search the results for new warnings and check for false // positives. +#![feature(iter_collect_into)] #![warn( trivial_casts, trivial_numeric_casts, @@ -12,84 +13,38 @@ unused_lifetimes, unused_qualifications )] -#![allow(clippy::collapsible_else_if, clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::collapsible_else_if, + clippy::needless_borrows_for_generic_args, + clippy::module_name_repetitions +)] mod config; mod driver; +mod input; mod json; +mod output; mod popular_crates; mod recursive; use crate::config::{Commands, LintcheckConfig, OutputFormat}; use crate::recursive::LintcheckServer; -use std::collections::{HashMap, HashSet}; use std::env::consts::EXE_SUFFIX; -use std::fmt::{self, Display, Write as _}; -use std::hash::Hash; -use std::io::{self, ErrorKind}; +use std::io::{self}; use std::path::{Path, PathBuf}; -use std::process::{Command, ExitStatus, Stdio}; +use std::process::{Command, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::Duration; -use std::{env, fs, thread}; +use std::{env, fs}; -use cargo_metadata::diagnostic::{Diagnostic, DiagnosticSpan}; use cargo_metadata::Message; +use input::{read_crates, CrateSource}; +use output::{ClippyCheckOutput, ClippyWarning, RustcIce}; use rayon::prelude::*; -use serde::{Deserialize, Serialize}; -use walkdir::{DirEntry, WalkDir}; const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; -/// List of sources to check, loaded from a .toml file -#[derive(Debug, Deserialize)] -struct SourceList { - crates: HashMap, - #[serde(default)] - recursive: RecursiveOptions, -} - -#[derive(Debug, Deserialize, Default)] -struct RecursiveOptions { - ignore: HashSet, -} - -/// A crate source stored inside the .toml -/// will be translated into on one of the `CrateSource` variants -#[derive(Debug, Deserialize)] -struct TomlCrate { - name: String, - versions: Option>, - git_url: Option, - git_hash: Option, - path: Option, - options: Option>, -} - -/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder -/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` -#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] -enum CrateSource { - CratesIo { - name: String, - version: String, - options: Option>, - }, - Git { - name: String, - url: String, - commit: String, - options: Option>, - }, - Path { - name: String, - path: PathBuf, - options: Option>, - }, -} - /// Represents the actual source code of a crate that we ran "cargo clippy" on #[derive(Debug)] struct Crate { @@ -100,248 +55,6 @@ struct Crate { options: Option>, } -/// A single emitted output from clippy being executed on a crate. It may either be a -/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many -/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution). -#[derive(Debug)] -enum ClippyCheckOutput { - ClippyWarning(ClippyWarning), - RustcIce(RustcIce), -} - -#[derive(Debug)] -struct RustcIce { - pub crate_name: String, - pub ice_content: String, -} - -impl Display for RustcIce { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}:\n{}\n========================================\n", - self.crate_name, self.ice_content - ) - } -} - -impl RustcIce { - pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option { - if status.code().unwrap_or(0) == 101 - /* ice exit status */ - { - Some(Self { - crate_name: crate_name.to_owned(), - ice_content: stderr.to_owned(), - }) - } else { - None - } - } -} - -/// A single warning that clippy issued while checking a `Crate` -#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -struct ClippyWarning { - crate_name: String, - crate_version: String, - lint_type: String, - diag: Diagnostic, -} - -#[allow(unused)] -impl ClippyWarning { - fn new(mut diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option { - let lint_type = diag.code.clone()?.code; - if !(lint_type.contains("clippy") || diag.message.contains("clippy")) - || diag.message.contains("could not read cargo metadata") - { - return None; - } - - // --recursive bypasses cargo so we have to strip the rendered output ourselves - let rendered = diag.rendered.as_mut().unwrap(); - *rendered = strip_ansi_escapes::strip_str(&rendered); - - Some(Self { - crate_name: crate_name.to_owned(), - crate_version: crate_version.to_owned(), - lint_type, - diag, - }) - } - - fn span(&self) -> &DiagnosticSpan { - self.diag.spans.iter().find(|span| span.is_primary).unwrap() - } - - fn to_output(&self, format: OutputFormat) -> String { - let span = self.span(); - let mut file = span.file_name.clone(); - let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end); - match format { - OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.diag.message), - OutputFormat::Markdown => { - if file.starts_with("target") { - file.insert_str(0, "../"); - } - - let mut output = String::from("| "); - write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap(); - write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.diag.message).unwrap(); - output.push('\n'); - output - }, - OutputFormat::Json => unreachable!("JSON output is handled via serde"), - } - } -} - -#[allow(clippy::result_large_err)] -fn get(path: &str) -> Result { - const MAX_RETRIES: u8 = 4; - let mut retries = 0; - loop { - match ureq::get(path).call() { - Ok(res) => return Ok(res), - Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), - Err(e) => return Err(e), - } - eprintln!("retrying in {retries} seconds..."); - thread::sleep(Duration::from_secs(u64::from(retries))); - retries += 1; - } -} - -impl CrateSource { - /// Makes the sources available on the disk for clippy to check. - /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or - /// copies a local folder - fn download_and_extract(&self) -> Crate { - match self { - CrateSource::CratesIo { name, version, options } => { - let extract_dir = PathBuf::from(LINTCHECK_SOURCES); - let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); - - // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); - println!("Downloading and extracting {name} {version} from {url}"); - create_dirs(&krate_download_dir, &extract_dir); - - let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); - // don't download/extract if we already have done so - if !krate_file_path.is_file() { - // create a file path to download and write the crate data into - let mut krate_dest = fs::File::create(&krate_file_path).unwrap(); - let mut krate_req = get(&url).unwrap().into_reader(); - // copy the crate into the file - io::copy(&mut krate_req, &mut krate_dest).unwrap(); - - // unzip the tarball - let ungz_tar = flate2::read::GzDecoder::new(fs::File::open(&krate_file_path).unwrap()); - // extract the tar archive - let mut archive = tar::Archive::new(ungz_tar); - archive.unpack(&extract_dir).expect("Failed to extract!"); - } - // crate is extracted, return a new Krate object which contains the path to the extracted - // sources that clippy can check - Crate { - version: version.clone(), - name: name.clone(), - path: extract_dir.join(format!("{name}-{version}/")), - options: options.clone(), - } - }, - CrateSource::Git { - name, - url, - commit, - options, - } => { - let repo_path = { - let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); - // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{name}-git")); - repo_path - }; - // clone the repo if we have not done so - if !repo_path.is_dir() { - println!("Cloning {url} and checking out {commit}"); - if !Command::new("git") - .arg("clone") - .arg(url) - .arg(&repo_path) - .status() - .expect("Failed to clone git repo!") - .success() - { - eprintln!("Failed to clone {url} into {}", repo_path.display()); - } - } - // check out the commit/branch/whatever - if !Command::new("git") - .args(["-c", "advice.detachedHead=false"]) - .arg("checkout") - .arg(commit) - .current_dir(&repo_path) - .status() - .expect("Failed to check out commit") - .success() - { - eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); - } - - Crate { - version: commit.clone(), - name: name.clone(), - path: repo_path, - options: options.clone(), - } - }, - CrateSource::Path { name, path, options } => { - fn is_cache_dir(entry: &DirEntry) -> bool { - fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } - - // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. - // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory - // as a result of this filter. - let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); - if dest_crate_root.exists() { - println!("Deleting existing directory at {dest_crate_root:?}"); - fs::remove_dir_all(&dest_crate_root).unwrap(); - } - - println!("Copying {path:?} to {dest_crate_root:?}"); - - for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { - let entry = entry.unwrap(); - let entry_path = entry.path(); - let relative_entry_path = entry_path.strip_prefix(path).unwrap(); - let dest_path = dest_crate_root.join(relative_entry_path); - let metadata = entry_path.symlink_metadata().unwrap(); - - if metadata.is_dir() { - fs::create_dir(dest_path).unwrap(); - } else if metadata.is_file() { - fs::copy(entry_path, dest_path).unwrap(); - } - } - - Crate { - version: String::from("local"), - name: name.clone(), - path: dest_crate_root, - options: options.clone(), - } - }, - } - } -} - impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued @@ -352,7 +65,7 @@ impl Crate { target_dir_index: &AtomicUsize, total_crates_to_lint: usize, config: &LintcheckConfig, - lint_filter: &[String], + lint_levels_args: &[String], server: &Option, ) -> Vec { // advance the atomic index by one @@ -398,16 +111,9 @@ impl Crate { for opt in options { clippy_args.push(opt); } - } else { - clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } - if lint_filter.is_empty() { - clippy_args.push("--cap-lints=warn"); - } else { - clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(String::as_str)); - } + clippy_args.extend(lint_levels_args.iter().map(String::as_str)); let mut cmd = Command::new("cargo"); cmd.arg(if config.fix { "fix" } else { "check" }) @@ -479,7 +185,7 @@ impl Crate { // get all clippy warnings and ICEs let mut entries: Vec = Message::parse_stream(stdout.as_bytes()) .filter_map(|msg| match msg { - Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version), + Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message), _ => None, }) .map(ClippyCheckOutput::ClippyWarning) @@ -509,96 +215,6 @@ fn build_clippy() -> String { String::from_utf8_lossy(&output.stdout).into_owned() } -/// Read a `lintcheck_crates.toml` file -fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { - let toml_content: String = - fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); - let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); - // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec = crate_list.crates.into_values().collect(); - - // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => - // multiple Cratesources) - let mut crate_sources = Vec::new(); - for tk in tomlcrates { - if let Some(ref path) = tk.path { - crate_sources.push(CrateSource::Path { - name: tk.name.clone(), - path: PathBuf::from(path), - options: tk.options.clone(), - }); - } else if let Some(ref versions) = tk.versions { - // if we have multiple versions, save each one - for ver in versions { - crate_sources.push(CrateSource::CratesIo { - name: tk.name.clone(), - version: ver.to_string(), - options: tk.options.clone(), - }); - } - } else if tk.git_url.is_some() && tk.git_hash.is_some() { - // otherwise, we should have a git source - crate_sources.push(CrateSource::Git { - name: tk.name.clone(), - url: tk.git_url.clone().unwrap(), - commit: tk.git_hash.clone().unwrap(), - options: tk.options.clone(), - }); - } else { - panic!("Invalid crate source: {tk:?}"); - } - - // if we have a version as well as a git data OR only one git data, something is funky - if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) - || tk.git_hash.is_some() != tk.git_url.is_some() - { - eprintln!("tomlkrate: {tk:?}"); - assert_eq!( - tk.git_hash.is_some(), - tk.git_url.is_some(), - "Error: Encountered TomlCrate with only one of git_hash and git_url!" - ); - assert!( - tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), - "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" - ); - unreachable!("Failed to translate TomlCrate into CrateSource!"); - } - } - // sort the crates - crate_sources.sort(); - - (crate_sources, crate_list.recursive) -} - -/// Generate a short list of occurring lints-types and their count -fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { - // count lint type occurrences - let mut counter: HashMap<&String, usize> = HashMap::new(); - warnings - .iter() - .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1); - - // collect into a tupled list for sorting - let mut stats: Vec<(&&String, &usize)> = counter.iter().collect(); - // sort by "000{count} {clippy::lintname}" - // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); - - let mut header = String::from("| lint | count |\n"); - header.push_str("| -------------------------------------------------- | ----- |\n"); - let stats_string = stats - .iter() - .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) - .fold(header, |mut table, line| { - table.push_str(&line); - table - }); - - (stats_string, counter) -} - fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -638,15 +254,39 @@ fn lintcheck(config: LintcheckConfig) { let (crates, recursive_options) = read_crates(&config.sources_toml_path); let counter = AtomicUsize::new(1); - let lint_filter: Vec = config - .lint_filter - .iter() - .map(|filter| { - let mut filter = filter.clone(); - filter.insert_str(0, "--force-warn="); - filter - }) - .collect(); + let mut lint_level_args: Vec = vec![]; + if config.lint_filter.is_empty() { + lint_level_args.push("--cap-lints=warn".to_string()); + + // Set allow-by-default to warn + if config.warn_all { + [ + "clippy::cargo", + "clippy::nursery", + "clippy::pedantic", + "clippy::restriction", + ] + .iter() + .map(|group| format!("--warn={group}")) + .collect_into(&mut lint_level_args); + } else { + ["clippy::cargo", "clippy::pedantic"] + .iter() + .map(|group| format!("--warn={group}")) + .collect_into(&mut lint_level_args); + } + } else { + lint_level_args.push("--cap-lints=allow".to_string()); + config + .lint_filter + .iter() + .map(|filter| { + let mut filter = filter.clone(); + filter.insert_str(0, "--force-warn="); + filter + }) + .collect_into(&mut lint_level_args); + }; let crates: Vec = crates .into_iter() @@ -698,7 +338,7 @@ fn lintcheck(config: LintcheckConfig) { &counter, crates.len(), &config, - &lint_filter, + &lint_level_args, &server, ) }) @@ -727,7 +367,9 @@ fn lintcheck(config: LintcheckConfig) { } let text = match config.format { - OutputFormat::Text | OutputFormat::Markdown => output(&warnings, &raw_ices, clippy_ver, &config), + OutputFormat::Text | OutputFormat::Markdown => { + output::summarize_and_print_changes(&warnings, &raw_ices, clippy_ver, &config) + }, OutputFormat::Json => { if !raw_ices.is_empty() { for ice in raw_ices { @@ -736,7 +378,7 @@ fn lintcheck(config: LintcheckConfig) { panic!("Some crates ICEd"); } - json::output(&warnings) + json::output(warnings) }, }; @@ -745,135 +387,6 @@ fn lintcheck(config: LintcheckConfig) { fs::write(&config.lintcheck_results_path, text).unwrap(); } -/// Creates the log file output for [`OutputFormat::Text`] and [`OutputFormat::Markdown`] -fn output(warnings: &[ClippyWarning], ices: &[RustcIce], clippy_ver: String, config: &LintcheckConfig) -> String { - // generate some stats - let (stats_formatted, new_stats) = gather_stats(warnings); - let old_stats = read_stats_from_file(&config.lintcheck_results_path); - - let mut all_msgs: Vec = warnings.iter().map(|warn| warn.to_output(config.format)).collect(); - all_msgs.sort(); - all_msgs.push("\n\n### Stats:\n\n".into()); - all_msgs.push(stats_formatted); - - let mut text = clippy_ver; // clippy version number on top - text.push_str("\n### Reports\n\n"); - if config.format == OutputFormat::Markdown { - text.push_str("| file | lint | message |\n"); - text.push_str("| --- | --- | --- |\n"); - } - write!(text, "{}", all_msgs.join("")).unwrap(); - text.push_str("\n\n### ICEs:\n"); - for ice in ices { - writeln!(text, "{ice}").unwrap(); - } - - print_stats(old_stats, new_stats, &config.lint_filter); - - text -} - -/// read the previous stats from the lintcheck-log file -fn read_stats_from_file(file_path: &Path) -> HashMap { - let file_content: String = match fs::read_to_string(file_path).ok() { - Some(content) => content, - None => { - return HashMap::new(); - }, - }; - - let lines: Vec = file_content.lines().map(ToString::to_string).collect(); - - lines - .iter() - .skip_while(|line| line.as_str() != "### Stats:") - // Skipping the table header and the `Stats:` label - .skip(4) - .take_while(|line| line.starts_with("| ")) - .filter_map(|line| { - let mut spl = line.split('|'); - // Skip the first `|` symbol - spl.next(); - if let (Some(lint), Some(count)) = (spl.next(), spl.next()) { - Some((lint.trim().to_string(), count.trim().parse::().unwrap())) - } else { - None - } - }) - .collect::>() -} - -/// print how lint counts changed between runs -fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &[String]) { - let same_in_both_hashmaps = old_stats - .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) - .map(|(k, v)| (k.to_string(), *v)) - .collect::>(); - - let mut old_stats_deduped = old_stats; - let mut new_stats_deduped = new_stats; - - // remove duplicates from both hashmaps - for (k, v) in &same_in_both_hashmaps { - assert!(old_stats_deduped.remove(k) == Some(*v)); - assert!(new_stats_deduped.remove(k) == Some(*v)); - } - - println!("\nStats:"); - - // list all new counts (key is in new stats but not in old stats) - new_stats_deduped - .iter() - .filter(|(new_key, _)| !old_stats_deduped.contains_key::(new_key)) - .for_each(|(new_key, new_value)| { - println!("{new_key} 0 => {new_value}"); - }); - - // list all changed counts (key is in both maps but value differs) - new_stats_deduped - .iter() - .filter(|(new_key, _new_val)| old_stats_deduped.contains_key::(new_key)) - .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(new_key).unwrap(); - println!("{new_key} {old_val} => {new_val}"); - }); - - // list all gone counts (key is in old status but not in new stats) - old_stats_deduped - .iter() - .filter(|(old_key, _)| !new_stats_deduped.contains_key::<&String>(old_key)) - .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) - .for_each(|(old_key, old_value)| { - println!("{old_key} {old_value} => 0"); - }); -} - -/// Create necessary directories to run the lintcheck tool. -/// -/// # Panics -/// -/// This function panics if creating one of the dirs fails. -fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { - fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - assert_eq!( - err.kind(), - ErrorKind::AlreadyExists, - "cannot create lintcheck target dir" - ); - }); - fs::create_dir(krate_download_dir).unwrap_or_else(|err| { - assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); - }); - fs::create_dir(extract_dir).unwrap_or_else(|err| { - assert_eq!( - err.kind(), - ErrorKind::AlreadyExists, - "cannot create crate extraction dir" - ); - }); -} - /// Returns the path to the Clippy project directory #[must_use] fn clippy_project_root() -> &'static Path { diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs new file mode 100644 index 0000000000000..4bfc554ef9e61 --- /dev/null +++ b/lintcheck/src/output.rs @@ -0,0 +1,235 @@ +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticSpan}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fmt::{self, Write as _}; +use std::fs; +use std::path::Path; +use std::process::ExitStatus; + +use crate::config::{LintcheckConfig, OutputFormat}; + +/// A single emitted output from clippy being executed on a crate. It may either be a +/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many +/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution). +#[derive(Debug)] +pub enum ClippyCheckOutput { + ClippyWarning(ClippyWarning), + RustcIce(RustcIce), +} + +#[derive(Debug)] +pub struct RustcIce { + pub crate_name: String, + pub ice_content: String, +} + +impl fmt::Display for RustcIce { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}:\n{}\n========================================\n", + self.crate_name, self.ice_content + ) + } +} + +impl RustcIce { + pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option { + if status.code().unwrap_or(0) == 101 + /* ice exit status */ + { + Some(Self { + crate_name: crate_name.to_owned(), + ice_content: stderr.to_owned(), + }) + } else { + None + } + } +} + +/// A single warning that clippy issued while checking a `Crate` +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ClippyWarning { + pub lint: String, + pub diag: Diagnostic, +} + +#[allow(unused)] +impl ClippyWarning { + pub fn new(mut diag: Diagnostic) -> Option { + let lint = diag.code.clone()?.code; + if !(lint.contains("clippy") || diag.message.contains("clippy")) + || diag.message.contains("could not read cargo metadata") + { + return None; + } + + // --recursive bypasses cargo so we have to strip the rendered output ourselves + let rendered = diag.rendered.as_mut().unwrap(); + *rendered = strip_ansi_escapes::strip_str(&rendered); + + Some(Self { lint, diag }) + } + + pub fn span(&self) -> &DiagnosticSpan { + self.diag.spans.iter().find(|span| span.is_primary).unwrap() + } + + pub fn to_output(&self, format: OutputFormat) -> String { + let span = self.span(); + let mut file = span.file_name.clone(); + let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end); + match format { + OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message), + OutputFormat::Markdown => { + if file.starts_with("target") { + file.insert_str(0, "../"); + } + + let mut output = String::from("| "); + write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap(); + write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap(); + output.push('\n'); + output + }, + OutputFormat::Json => unreachable!("JSON output is handled via serde"), + } + } +} + +/// Creates the log file output for [`OutputFormat::Text`] and [`OutputFormat::Markdown`] +pub fn summarize_and_print_changes( + warnings: &[ClippyWarning], + ices: &[RustcIce], + clippy_ver: String, + config: &LintcheckConfig, +) -> String { + // generate some stats + let (stats_formatted, new_stats) = gather_stats(warnings); + let old_stats = read_stats_from_file(&config.lintcheck_results_path); + + let mut all_msgs: Vec = warnings.iter().map(|warn| warn.to_output(config.format)).collect(); + all_msgs.sort(); + all_msgs.push("\n\n### Stats:\n\n".into()); + all_msgs.push(stats_formatted); + + let mut text = clippy_ver; // clippy version number on top + text.push_str("\n### Reports\n\n"); + if config.format == OutputFormat::Markdown { + text.push_str("| file | lint | message |\n"); + text.push_str("| --- | --- | --- |\n"); + } + write!(text, "{}", all_msgs.join("")).unwrap(); + text.push_str("\n\n### ICEs:\n"); + for ice in ices { + writeln!(text, "{ice}").unwrap(); + } + + print_stats(old_stats, new_stats, &config.lint_filter); + + text +} + +/// Generate a short list of occurring lints-types and their count +fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { + // count lint type occurrences + let mut counter: HashMap<&String, usize> = HashMap::new(); + warnings + .iter() + .for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1); + + // collect into a tupled list for sorting + let mut stats: Vec<(&&String, &usize)> = counter.iter().collect(); + // sort by "000{count} {clippy::lintname}" + // to not have a lint with 200 and 2 warnings take the same spot + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); + + let mut header = String::from("| lint | count |\n"); + header.push_str("| -------------------------------------------------- | ----- |\n"); + let stats_string = stats + .iter() + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) + .fold(header, |mut table, line| { + table.push_str(&line); + table + }); + + (stats_string, counter) +} + +/// read the previous stats from the lintcheck-log file +fn read_stats_from_file(file_path: &Path) -> HashMap { + let file_content: String = match fs::read_to_string(file_path).ok() { + Some(content) => content, + None => { + return HashMap::new(); + }, + }; + + let lines: Vec = file_content.lines().map(ToString::to_string).collect(); + + lines + .iter() + .skip_while(|line| line.as_str() != "### Stats:") + // Skipping the table header and the `Stats:` label + .skip(4) + .take_while(|line| line.starts_with("| ")) + .filter_map(|line| { + let mut spl = line.split('|'); + // Skip the first `|` symbol + spl.next(); + if let (Some(lint), Some(count)) = (spl.next(), spl.next()) { + Some((lint.trim().to_string(), count.trim().parse::().unwrap())) + } else { + None + } + }) + .collect::>() +} + +/// print how lint counts changed between runs +fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &[String]) { + let same_in_both_hashmaps = old_stats + .iter() + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) + .map(|(k, v)| (k.to_string(), *v)) + .collect::>(); + + let mut old_stats_deduped = old_stats; + let mut new_stats_deduped = new_stats; + + // remove duplicates from both hashmaps + for (k, v) in &same_in_both_hashmaps { + assert!(old_stats_deduped.remove(k) == Some(*v)); + assert!(new_stats_deduped.remove(k) == Some(*v)); + } + + println!("\nStats:"); + + // list all new counts (key is in new stats but not in old stats) + new_stats_deduped + .iter() + .filter(|(new_key, _)| !old_stats_deduped.contains_key::(new_key)) + .for_each(|(new_key, new_value)| { + println!("{new_key} 0 => {new_value}"); + }); + + // list all changed counts (key is in both maps but value differs) + new_stats_deduped + .iter() + .filter(|(new_key, _new_val)| old_stats_deduped.contains_key::(new_key)) + .for_each(|(new_key, new_val)| { + let old_val = old_stats_deduped.get::(new_key).unwrap(); + println!("{new_key} {old_val} => {new_val}"); + }); + + // list all gone counts (key is in old status but not in new stats) + old_stats_deduped + .iter() + .filter(|(old_key, _)| !new_stats_deduped.contains_key::<&String>(old_key)) + .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) + .for_each(|(old_key, old_value)| { + println!("{old_key} {old_value} => 0"); + }); +} diff --git a/lintcheck/src/popular_crates.rs b/lintcheck/src/popular_crates.rs index 880a8bd81f083..ad8fc440c4240 100644 --- a/lintcheck/src/popular_crates.rs +++ b/lintcheck/src/popular_crates.rs @@ -44,7 +44,7 @@ pub(crate) fn fetch(output: PathBuf, number: usize) -> Result<(), Box let mut out = "[crates]\n".to_string(); for Crate { name, max_version } in crates { - writeln!(out, "{name} = {{ name = '{name}', versions = ['{max_version}'] }}").unwrap(); + writeln!(out, "{name} = {{ name = '{name}', version = '{max_version}' }}").unwrap(); } fs::write(output, out)?; diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 994fa3c3b239a..373ca6f991841 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -3,7 +3,8 @@ //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running //! clippy on the crate to the server -use crate::{ClippyWarning, RecursiveOptions}; +use crate::input::RecursiveOptions; +use crate::ClippyWarning; use std::collections::HashSet; use std::io::{BufRead, BufReader, Read, Write}; @@ -19,8 +20,6 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] pub(crate) struct DriverInfo { pub package_name: String, - pub crate_name: String, - pub version: String, } pub(crate) fn serialize_line(value: &T, writer: &mut W) @@ -65,7 +64,7 @@ fn process_stream( let messages = stderr .lines() .filter_map(|json_msg| serde_json::from_str::(json_msg).ok()) - .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version)); + .filter_map(ClippyWarning::new); for message in messages { sender.send(message).unwrap(); diff --git a/rust-toolchain b/rust-toolchain index 72b50d59f7e9a..a61c22c59f980 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,4 @@ [toolchain] -channel = "nightly-2024-06-27" +channel = "nightly-2024-07-11" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +profile = "minimal" diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 1be4b665bcbac..66eda44f74556 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -1,22 +1,18 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> tests/ui-internal/disallow_span_lint.rs:14:5 + --> tests/ui-internal/disallow_span_lint.rs:14:8 | -LL | / cx.span_lint(lint, span, |lint| { -LL | | lint.primary_message(msg); -LL | | }); - | |______^ +LL | cx.span_lint(lint, span, |lint| { + | ^^^^^^^^^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml) = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:20:5 + --> tests/ui-internal/disallow_span_lint.rs:20:9 | -LL | / tcx.node_span_lint(lint, hir_id, span, |lint| { -LL | | lint.primary_message(msg); -LL | | }); - | |______^ +LL | tcx.node_span_lint(lint, hir_id, span, |lint| { + | ^^^^^^^^^^^^^^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml) diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index a74d8757e4a5f..016ee502c242f 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -1,4 +1,4 @@ -error: `std::string::String` may not be held across an `await` point per `clippy.toml` +error: `std::string::String` may not be held across an await point per `clippy.toml` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9 | LL | let _x = String::from("hello"); @@ -8,13 +8,13 @@ LL | let _x = String::from("hello"); = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]` -error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` +error: `std::net::Ipv4Addr` may not be held across an await point per `clippy.toml` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:10:9 | LL | let x = Ipv4Addr::new(127, 0, 0, 1); | ^ -error: `std::string::String` may not be held across an `await` point per `clippy.toml` +error: `std::string::String` may not be held across an await point per `clippy.toml` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:33:13 | LL | let _x = String::from("hi!"); diff --git a/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml b/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml new file mode 100644 index 0000000000000..cda8d17eed44c --- /dev/null +++ b/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml @@ -0,0 +1 @@ +avoid-breaking-exported-api = false diff --git a/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed new file mode 100644 index 0000000000000..40556ca5410f2 --- /dev/null +++ b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::needless_pass_by_ref_mut)] +#![allow(clippy::ptr_arg)] + +// Should warn +pub fn pub_foo(s: &Vec, b: &u32, x: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + *x += *b + s.len() as u32; +} + +fn main() {} diff --git a/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs new file mode 100644 index 0000000000000..bbc63ceb15a3c --- /dev/null +++ b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs @@ -0,0 +1,10 @@ +#![warn(clippy::needless_pass_by_ref_mut)] +#![allow(clippy::ptr_arg)] + +// Should warn +pub fn pub_foo(s: &mut Vec, b: &u32, x: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + *x += *b + s.len() as u32; +} + +fn main() {} diff --git a/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr new file mode 100644 index 0000000000000..c10607bf4bab8 --- /dev/null +++ b/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr @@ -0,0 +1,12 @@ +error: this argument is a mutable reference, but not used mutably + --> tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs:5:19 + | +LL | pub fn pub_foo(s: &mut Vec, b: &u32, x: &mut u32) { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` + | + = warning: changing this function will impact semver compatibility + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 4afbbf5f80791..f661e76cc746e 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -2,36 +2,36 @@ error: use of a disallowed method `regex::Regex::new` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ | = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `regex::Regex::is_match` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:8 | LL | re.is_match("abc"); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 | LL | a.iter().sum::(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^ error: use of a disallowed method `slice::sort_unstable` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:41:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:41:7 | LL | a.sort_unstable(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:44:13 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:44:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: use of a disallowed method `regex::Regex::new` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:47:61 @@ -55,37 +55,37 @@ error: use of a disallowed method `futures::stream::select_all` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:5 | LL | local_fn(); - | ^^^^^^^^^^ + | ^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:5 | LL | local_mod::f(); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:7 | LL | s.method(); - | ^^^^^^^^^^ + | ^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:7 | LL | s.provided_method(); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:61:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:61:7 | LL | s.implemented_method(); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 14 previous errors diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index 1309ae45d0ae5..957154e60dece 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -1,4 +1,4 @@ -#![allow(non_fmt_panics, clippy::needless_bool)] +#![allow(non_fmt_panics, clippy::needless_bool, clippy::eq_op)] macro_rules! assert_const { ($len:expr) => { @@ -49,7 +49,16 @@ fn main() { const _: () = assert!(true); //~^ ERROR: `assert!(true)` will be optimized out by the compiler + assert!(8 == (7 + 1)); + //~^ ERROR: `assert!(true)` will be optimized out by the compiler + // Don't lint if the value is dependent on a defined constant: const N: usize = 1024; const _: () = assert!(N.is_power_of_two()); } + +const _: () = { + assert!(true); + //~^ ERROR: `assert!(true)` will be optimized out by the compiler + assert!(8 == (7 + 1)); +}; diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 00f117c949206..e164a999c43ee 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -80,5 +80,21 @@ LL | const _: () = assert!(true); | = help: remove it -error: aborting due to 10 previous errors +error: `assert!(true)` will be optimized out by the compiler + --> tests/ui/assertions_on_constants.rs:52:5 + | +LL | assert!(8 == (7 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove it + +error: `assert!(true)` will be optimized out by the compiler + --> tests/ui/assertions_on_constants.rs:61:5 + | +LL | assert!(true); + | ^^^^^^^^^^^^^ + | + = help: remove it + +error: aborting due to 12 previous errors diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index 8e5510e6cd01c..cecf00c934fee 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -8,7 +8,7 @@ mod std_mutex { pub async fn bad(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } @@ -24,13 +24,13 @@ mod std_mutex { pub async fn bad_rw(x: &RwLock) -> u32 { let guard = x.read().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } pub async fn bad_rw_write(x: &RwLock) -> u32 { let mut guard = x.write().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } @@ -52,7 +52,7 @@ mod std_mutex { let first = baz().await; let guard = x.lock().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point let second = baz().await; @@ -66,7 +66,7 @@ mod std_mutex { let second = { let guard = x.lock().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await }; @@ -79,7 +79,7 @@ mod std_mutex { pub fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } } @@ -92,7 +92,7 @@ mod parking_lot_mutex { pub async fn bad(x: &Mutex) -> u32 { let guard = x.lock(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } @@ -108,13 +108,13 @@ mod parking_lot_mutex { pub async fn bad_rw(x: &RwLock) -> u32 { let guard = x.read(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } pub async fn bad_rw_write(x: &RwLock) -> u32 { let mut guard = x.write(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } @@ -136,7 +136,7 @@ mod parking_lot_mutex { let first = baz().await; let guard = x.lock(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point let second = baz().await; @@ -150,7 +150,7 @@ mod parking_lot_mutex { let second = { let guard = x.lock(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await }; @@ -163,7 +163,7 @@ mod parking_lot_mutex { pub fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point baz().await } } @@ -184,7 +184,7 @@ async fn no_await(x: std::sync::Mutex) { // `*guard += 1` is removed it is picked up. async fn dropped_before_await(x: std::sync::Mutex) { let mut guard = x.lock().unwrap(); - //~^ ERROR: this `MutexGuard` is held across an `await` point + //~^ ERROR: this `MutexGuard` is held across an await point *guard += 1; drop(guard); baz().await; diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 0af48a36acca1..af61d8939483f 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -1,11 +1,11 @@ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:10:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:12:15 | LL | baz().await @@ -13,40 +13,40 @@ LL | baz().await = note: `-D clippy::await-holding-lock` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]` -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:26:13 | LL | let guard = x.read().unwrap(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:28:15 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:32:13 | LL | let mut guard = x.write().unwrap(); | ^^^^^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:34:15 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:57:28 | LL | let second = baz().await; @@ -55,79 +55,79 @@ LL | LL | let third = baz().await; | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:68:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:70:19 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:81:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:83:19 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:94:13 | LL | let guard = x.lock(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:96:15 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:110:13 | LL | let guard = x.read(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:112:15 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:116:13 | LL | let mut guard = x.write(); | ^^^^^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:118:15 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:138:13 | LL | let guard = x.lock(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:141:28 | LL | let second = baz().await; @@ -136,40 +136,40 @@ LL | LL | let third = baz().await; | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:152:17 | LL | let guard = x.lock(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:154:19 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:165:17 | LL | let guard = x.lock(); | ^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:167:19 | LL | baz().await | ^^^^^ -error: this `MutexGuard` is held across an `await` point +error: this `MutexGuard` is held across an await point --> tests/ui/await_holding_lock.rs:186:9 | LL | let mut guard = x.lock().unwrap(); | ^^^^^^^^^ | - = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await -note: these are all the `await` points this lock is held through + = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await` +note: these are all the await points this lock is held through --> tests/ui/await_holding_lock.rs:190:11 | LL | baz().await; diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 5bd26c6283627..b0c92d8c1f6e6 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -4,13 +4,13 @@ use std::cell::RefCell; async fn bad(x: &RefCell) -> u32 { let b = x.borrow(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point baz().await } async fn bad_mut(x: &RefCell) -> u32 { let b = x.borrow_mut(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point baz().await } @@ -32,7 +32,7 @@ async fn also_bad(x: &RefCell) -> u32 { let first = baz().await; let b = x.borrow_mut(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point let second = baz().await; @@ -45,7 +45,7 @@ async fn less_bad(x: &RefCell) -> u32 { let first = baz().await; let b = x.borrow_mut(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point let second = baz().await; @@ -61,7 +61,7 @@ async fn not_good(x: &RefCell) -> u32 { let second = { let b = x.borrow_mut(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point baz().await }; @@ -74,7 +74,7 @@ async fn not_good(x: &RefCell) -> u32 { fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { async move { let b = x.borrow_mut(); - //~^ ERROR: this `RefCell` reference is held across an `await` point + //~^ ERROR: this `RefCell` reference is held across an await point baz().await } } diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index 6b474c27ddc18..6c7209c9ff9e7 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -1,11 +1,11 @@ -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:6:9 | LL | let b = x.borrow(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:8:11 | LL | baz().await @@ -13,27 +13,27 @@ LL | baz().await = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_refcell_ref)]` -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:12:9 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:14:11 | LL | baz().await | ^^^^^ -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:34:9 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:37:24 | LL | let second = baz().await; @@ -42,40 +42,40 @@ LL | LL | let third = baz().await; | ^^^^^ -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:47:9 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:50:24 | LL | let second = baz().await; | ^^^^^ -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:63:13 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:65:15 | LL | baz().await | ^^^^^ -error: this `RefCell` reference is held across an `await` point +error: this `RefCell` reference is held across an await point --> tests/ui/await_holding_refcell_ref.rs:76:13 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` -note: these are all the `await` points this reference is held through +note: these are all the await points this reference is held through --> tests/ui/await_holding_refcell_ref.rs:78:15 | LL | baz().await diff --git a/tests/ui/byte_char_slices.fixed b/tests/ui/byte_char_slices.fixed new file mode 100644 index 0000000000000..d1db58f9363e9 --- /dev/null +++ b/tests/ui/byte_char_slices.fixed @@ -0,0 +1,13 @@ +#![allow(unused)] +#![warn(clippy::byte_char_slices)] + +fn main() { + let bad = b"abc"; + let quotes = b"\"Hi"; + let quotes = b"'Sup"; + let escapes = b"\x42Esc"; + + let good = &[b'a', 0x42]; + let good = [b'a', b'a']; + let good: u8 = [b'a', b'c'].into_iter().sum(); +} diff --git a/tests/ui/byte_char_slices.rs b/tests/ui/byte_char_slices.rs new file mode 100644 index 0000000000000..18648fffceb40 --- /dev/null +++ b/tests/ui/byte_char_slices.rs @@ -0,0 +1,13 @@ +#![allow(unused)] +#![warn(clippy::byte_char_slices)] + +fn main() { + let bad = &[b'a', b'b', b'c']; + let quotes = &[b'"', b'H', b'i']; + let quotes = &[b'\'', b'S', b'u', b'p']; + let escapes = &[b'\x42', b'E', b's', b'c']; + + let good = &[b'a', 0x42]; + let good = vec![b'a', b'a']; + let good: u8 = [b'a', b'c'].into_iter().sum(); +} diff --git a/tests/ui/byte_char_slices.stderr b/tests/ui/byte_char_slices.stderr new file mode 100644 index 0000000000000..4e2b5d8a7329a --- /dev/null +++ b/tests/ui/byte_char_slices.stderr @@ -0,0 +1,38 @@ +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:5:15 + | +LL | let bad = &[b'a', b'b', b'c']; + | ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"` + | + = note: `-D clippy::byte-char-slices` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:6:18 + | +LL | let quotes = &[b'"', b'H', b'i']; + | ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:7:18 + | +LL | let quotes = &[b'\'', b'S', b'u', b'p']; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"` + +error: can be more succinctly written as a byte str + --> tests/ui/byte_char_slices.rs:8:19 + | +LL | let escapes = &[b'\x42', b'E', b's', b'c']; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"` + +error: useless use of `vec!` + --> tests/ui/byte_char_slices.rs:11:16 + | +LL | let good = vec![b'a', b'a']; + | ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/cfg_not_test.rs b/tests/ui/cfg_not_test.rs new file mode 100644 index 0000000000000..da3e29d289662 --- /dev/null +++ b/tests/ui/cfg_not_test.rs @@ -0,0 +1,32 @@ +#![allow(unused)] +#![warn(clippy::cfg_not_test)] + +fn important_check() {} + +fn main() { + // Statement + #[cfg(not(test))] + let answer = 42; + + // Expression + #[cfg(not(test))] + important_check(); + + // Make sure only not(test) are checked, not other attributes + #[cfg(not(foo))] + important_check(); +} + +#[cfg(not(not(test)))] +struct CfgNotTest; + +// Deeply nested `not(test)` +#[cfg(not(test))] +fn foo() {} +#[cfg(all(debug_assertions, not(test)))] +fn bar() {} +#[cfg(not(any(not(debug_assertions), test)))] +fn baz() {} + +#[cfg(test)] +mod tests {} diff --git a/tests/ui/cfg_not_test.stderr b/tests/ui/cfg_not_test.stderr new file mode 100644 index 0000000000000..c1bf626887afb --- /dev/null +++ b/tests/ui/cfg_not_test.stderr @@ -0,0 +1,45 @@ +error: code is excluded from test builds + --> tests/ui/cfg_not_test.rs:8:5 + | +LL | #[cfg(not(test))] + | ^^^^^^^^^^^^^^^^^ + | + = help: consider not excluding any code from test builds + = note: this could increase code coverage despite not actually being tested + = note: `-D clippy::cfg-not-test` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cfg_not_test)]` + +error: code is excluded from test builds + --> tests/ui/cfg_not_test.rs:12:5 + | +LL | #[cfg(not(test))] + | ^^^^^^^^^^^^^^^^^ + | + = help: consider not excluding any code from test builds + +error: code is excluded from test builds + --> tests/ui/cfg_not_test.rs:24:1 + | +LL | #[cfg(not(test))] + | ^^^^^^^^^^^^^^^^^ + | + = help: consider not excluding any code from test builds + +error: code is excluded from test builds + --> tests/ui/cfg_not_test.rs:26:1 + | +LL | #[cfg(all(debug_assertions, not(test)))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider not excluding any code from test builds + +error: code is excluded from test builds + --> tests/ui/cfg_not_test.rs:28:1 + | +LL | #[cfg(not(any(not(debug_assertions), test)))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider not excluding any code from test builds + +error: aborting due to 5 previous errors + diff --git a/tests/ui/crashes/ice-12616.stderr b/tests/ui/crashes/ice-12616.stderr index c7cf5cf5483f7..a84a945a429ff 100644 --- a/tests/ui/crashes/ice-12616.stderr +++ b/tests/ui/crashes/ice-12616.stderr @@ -1,4 +1,4 @@ -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/crashes/ice-12616.rs:6:5 | LL | s() as *const (); diff --git a/tests/ui/disallowed_names.rs b/tests/ui/disallowed_names.rs index 13c883409bf6e..96531bf8d88ca 100644 --- a/tests/ui/disallowed_names.rs +++ b/tests/ui/disallowed_names.rs @@ -60,6 +60,7 @@ fn issue_1647_ref_mut() { //~^ ERROR: use of a disallowed/placeholder name `quux` } +#[cfg(test)] mod tests { fn issue_7305() { // `disallowed_names` lint should not be triggered inside of the test code. diff --git a/tests/ui/doc/doc_lazy_blank_line.fixed b/tests/ui/doc/doc_lazy_blank_line.fixed new file mode 100644 index 0000000000000..1aaa26afe7f26 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blank_line.fixed @@ -0,0 +1,47 @@ +// https://github.com/rust-lang/rust-clippy/issues/12917 +#![warn(clippy::doc_lazy_continuation)] + +/// This is a constant. +/// +/// The meaning of which should not be explained. +pub const A: i32 = 42; + +/// This is another constant, no longer used. +/// +/// This block of documentation has a long +/// explanation and derivation to explain +/// why it is what it is, and how it's used. +/// +/// It is left here for historical reasons, and +/// for reference. +/// +/// Reasons it's great: +/// - First reason +/// - Second reason +/// +//pub const B: i32 = 1337; + +/// This is yet another constant. +/// +/// This has a similar fate as `B`. +/// +/// Reasons it's useful: +/// 1. First reason +/// 2. Second reason +/// +//pub const C: i32 = 8008; + +/// This is still in use. +pub const D: i32 = 20; + +/// > blockquote code path +/// + +/// bottom text +pub const E: i32 = 20; + +/// > blockquote code path +/// +#[repr(C)] +/// bottom text +pub struct Foo(i32); diff --git a/tests/ui/doc/doc_lazy_blank_line.rs b/tests/ui/doc/doc_lazy_blank_line.rs new file mode 100644 index 0000000000000..e1ab8fc838926 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blank_line.rs @@ -0,0 +1,43 @@ +// https://github.com/rust-lang/rust-clippy/issues/12917 +#![warn(clippy::doc_lazy_continuation)] + +/// This is a constant. +/// +/// The meaning of which should not be explained. +pub const A: i32 = 42; + +/// This is another constant, no longer used. +/// +/// This block of documentation has a long +/// explanation and derivation to explain +/// why it is what it is, and how it's used. +/// +/// It is left here for historical reasons, and +/// for reference. +/// +/// Reasons it's great: +/// - First reason +/// - Second reason +//pub const B: i32 = 1337; + +/// This is yet another constant. +/// +/// This has a similar fate as `B`. +/// +/// Reasons it's useful: +/// 1. First reason +/// 2. Second reason +//pub const C: i32 = 8008; + +/// This is still in use. +pub const D: i32 = 20; + +/// > blockquote code path + +/// bottom text +pub const E: i32 = 20; + +/// > blockquote code path +#[repr(C)] +/// bottom text +pub struct Foo(i32); diff --git a/tests/ui/doc/doc_lazy_blank_line.stderr b/tests/ui/doc/doc_lazy_blank_line.stderr new file mode 100644 index 0000000000000..854906a747418 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blank_line.stderr @@ -0,0 +1,56 @@ +error: doc list item without indentation + --> tests/ui/doc/doc_lazy_blank_line.rs:23:5 + | +LL | /// This is yet another constant. + | ^ + | + = help: if this is intended to be part of the list, indent 3 spaces + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// - Second reason +LL + /// + | + +error: doc list item without indentation + --> tests/ui/doc/doc_lazy_blank_line.rs:32:5 + | +LL | /// This is still in use. + | ^ + | + = help: if this is intended to be part of the list, indent 4 spaces +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// 2. Second reason +LL + /// + | + +error: doc quote line without `>` marker + --> tests/ui/doc/doc_lazy_blank_line.rs:37:5 + | +LL | /// bottom text + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// > blockquote code path +LL + /// + | + +error: doc quote line without `>` marker + --> tests/ui/doc/doc_lazy_blank_line.rs:42:5 + | +LL | /// bottom text + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// > blockquote code path +LL + /// + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/doc/doc_lazy_blockquote.fixed b/tests/ui/doc/doc_lazy_blockquote.fixed index 9877991f183a5..9d6e8637608fb 100644 --- a/tests/ui/doc/doc_lazy_blockquote.fixed +++ b/tests/ui/doc/doc_lazy_blockquote.fixed @@ -2,7 +2,7 @@ /// > blockquote with /// > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn first() {} /// > blockquote with no @@ -18,24 +18,24 @@ fn two_nowarn() {} /// > /// > > nest here /// > > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn two() {} /// > nest here /// > /// > > nest here /// > > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn three() {} /// > * > nest here /// > > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn four() {} /// > * > nest here /// > > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn four_point_1() {} /// * > nest here lazy continuation @@ -43,5 +43,5 @@ fn five() {} /// 1. > nest here /// > lazy continuation (this results in strange indentation, but still works) -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.rs b/tests/ui/doc/doc_lazy_blockquote.rs index 587b2fdd533ce..0323a1b44e7cc 100644 --- a/tests/ui/doc/doc_lazy_blockquote.rs +++ b/tests/ui/doc/doc_lazy_blockquote.rs @@ -2,7 +2,7 @@ /// > blockquote with /// lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn first() {} /// > blockquote with no @@ -18,24 +18,24 @@ fn two_nowarn() {} /// > /// > > nest here /// > lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn two() {} /// > nest here /// > /// > > nest here /// lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn three() {} /// > * > nest here /// lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn four() {} /// > * > nest here /// lazy continuation -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn four_point_1() {} /// * > nest here lazy continuation @@ -43,5 +43,5 @@ fn five() {} /// 1. > nest here /// lazy continuation (this results in strange indentation, but still works) -//~^ ERROR: doc quote missing `>` marker +//~^ ERROR: doc quote line without `>` marker fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.stderr b/tests/ui/doc/doc_lazy_blockquote.stderr index 975184a01c3f0..d3390efdff375 100644 --- a/tests/ui/doc/doc_lazy_blockquote.stderr +++ b/tests/ui/doc/doc_lazy_blockquote.stderr @@ -1,4 +1,4 @@ -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:4:5 | LL | /// lazy continuation @@ -12,7 +12,7 @@ help: add markers to start of line LL | /// > lazy continuation | + -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:20:5 | LL | /// > lazy continuation @@ -24,7 +24,7 @@ help: add markers to start of line LL | /// > > lazy continuation | + -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:27:5 | LL | /// lazy continuation @@ -36,7 +36,7 @@ help: add markers to start of line LL | /// > > lazy continuation | +++ -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:32:5 | LL | /// lazy continuation @@ -48,7 +48,7 @@ help: add markers to start of line LL | /// > > lazy continuation | +++++++ -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:37:5 | LL | /// lazy continuation @@ -60,7 +60,7 @@ help: add markers to start of line LL | /// > > lazy continuation | +++++ -error: doc quote missing `>` marker +error: doc quote line without `>` marker --> tests/ui/doc/doc_lazy_blockquote.rs:45:5 | LL | /// lazy continuation (this results in strange indentation, but still works) diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed index 409e6b0bc227f..ea59ae4c01c96 100644 --- a/tests/ui/doc/doc_lazy_list.fixed +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -2,38 +2,41 @@ /// 1. nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn one() {} /// 1. first line /// lazy list continuations don't make warnings with this lint -//~^ ERROR: doc list item missing indentation -/// because they don't have the -//~^ ERROR: doc list item missing indentation +/// +//~^ ERROR: doc list item without indentation +/// because they don't have the +//~^ ERROR: doc list item without indentation fn two() {} /// - nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn three() {} /// - first line /// lazy list continuations don't make warnings with this lint -//~^ ERROR: doc list item missing indentation -/// because they don't have the -//~^ ERROR: doc list item missing indentation +/// +//~^ ERROR: doc list item without indentation +/// because they don't have the +//~^ ERROR: doc list item without indentation fn four() {} /// - nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn five() {} /// - - first line /// this will warn on the lazy continuation -//~^ ERROR: doc list item missing indentation -/// and so should this -//~^ ERROR: doc list item missing indentation +/// +//~^ ERROR: doc list item without indentation +/// and so should this +//~^ ERROR: doc list item without indentation fn six() {} /// - - first line @@ -54,7 +57,7 @@ fn seven() {} /// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors /// to set up. Example: /// 'protocol_descriptors': [ -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation /// { /// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP /// 'params': [ @@ -73,5 +76,5 @@ fn seven() {} /// }] /// } /// ] -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn eight() {} diff --git a/tests/ui/doc/doc_lazy_list.rs b/tests/ui/doc/doc_lazy_list.rs index 30ab448a1130c..3cc18e35780a2 100644 --- a/tests/ui/doc/doc_lazy_list.rs +++ b/tests/ui/doc/doc_lazy_list.rs @@ -2,38 +2,38 @@ /// 1. nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn one() {} /// 1. first line /// lazy list continuations don't make warnings with this lint -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation /// because they don't have the -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn two() {} /// - nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn three() {} /// - first line /// lazy list continuations don't make warnings with this lint -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation /// because they don't have the -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn four() {} /// - nest here /// lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn five() {} /// - - first line /// this will warn on the lazy continuation -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation /// and so should this -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn six() {} /// - - first line @@ -54,7 +54,7 @@ fn seven() {} /// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors /// to set up. Example: /// 'protocol_descriptors': [ -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation /// { /// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP /// 'params': [ @@ -73,5 +73,5 @@ fn seven() {} /// }] /// } /// ] -//~^ ERROR: doc list item missing indentation +//~^ ERROR: doc list item without indentation fn eight() {} diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr index ddfdc49340c45..52aa74df8948c 100644 --- a/tests/ui/doc/doc_lazy_list.stderr +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -1,4 +1,4 @@ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:4:5 | LL | /// lazy continuation @@ -12,7 +12,7 @@ help: indent this line LL | /// lazy continuation | +++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:9:5 | LL | /// lazy list continuations don't make warnings with this lint @@ -24,19 +24,20 @@ help: indent this line LL | /// lazy list continuations don't make warnings with this lint | +++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:11:5 | LL | /// because they don't have the | ^ | - = help: if this is supposed to be its own paragraph, add a blank line -help: indent this line + = help: if this is intended to be part of the list, indent 3 spaces +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// lazy list continuations don't make warnings with this lint +LL + /// | -LL | /// because they don't have the - | +++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:16:5 | LL | /// lazy continuation @@ -48,7 +49,7 @@ help: indent this line LL | /// lazy continuation | ++++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:21:5 | LL | /// lazy list continuations don't make warnings with this lint @@ -60,19 +61,20 @@ help: indent this line LL | /// lazy list continuations don't make warnings with this lint | ++++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:23:5 | LL | /// because they don't have the | ^ | - = help: if this is supposed to be its own paragraph, add a blank line -help: indent this line + = help: if this is intended to be part of the list, indent 4 spaces +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// lazy list continuations don't make warnings with this lint +LL + /// | -LL | /// because they don't have the - | ++++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:28:5 | LL | /// lazy continuation @@ -84,7 +86,7 @@ help: indent this line LL | /// lazy continuation | ++++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:33:5 | LL | /// this will warn on the lazy continuation @@ -96,19 +98,20 @@ help: indent this line LL | /// this will warn on the lazy continuation | ++++++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:35:5 | LL | /// and so should this | ^^^^ | - = help: if this is supposed to be its own paragraph, add a blank line -help: indent this line + = help: if this is intended to be part of the list, indent 2 spaces +help: if this should be its own paragraph, add a blank doc comment line + | +LL ~ /// this will warn on the lazy continuation +LL + /// | -LL | /// and so should this - | ++ -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:56:5 | LL | /// 'protocol_descriptors': [ @@ -120,7 +123,7 @@ help: indent this line LL | /// 'protocol_descriptors': [ | + -error: doc list item missing indentation +error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:75:5 | LL | /// ] diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs index 6f7bab72040c8..04446787b6c29 100644 --- a/tests/ui/doc/unbalanced_ticks.rs +++ b/tests/ui/doc/unbalanced_ticks.rs @@ -49,3 +49,20 @@ fn other_markdown() {} /// pub struct Struct; /// ``` fn issue_7421() {} + +/// ` +//~^ ERROR: backticks are unbalanced +fn escape_0() {} + +/// Escaped \` backticks don't count. +fn escape_1() {} + +/// Escaped \` \` backticks don't count. +fn escape_2() {} + +/// Escaped \` ` backticks don't count, but unescaped backticks do. +//~^ ERROR: backticks are unbalanced +fn escape_3() {} + +/// Backslashes ` \` within code blocks don't count. +fn escape_4() {} diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr index 56ef291362318..50324010e97f7 100644 --- a/tests/ui/doc/unbalanced_ticks.stderr +++ b/tests/ui/doc/unbalanced_ticks.stderr @@ -78,5 +78,21 @@ help: try LL | /// - This item needs `backticks_here` | ~~~~~~~~~~~~~~~~ -error: aborting due to 8 previous errors +error: backticks are unbalanced + --> tests/ui/doc/unbalanced_ticks.rs:53:5 + | +LL | /// ` + | ^ + | + = help: a backtick may be missing a pair + +error: backticks are unbalanced + --> tests/ui/doc/unbalanced_ticks.rs:63:5 + | +LL | /// Escaped \` ` backticks don't count, but unescaped backticks do. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: aborting due to 10 previous errors diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index e6ca4bb66ccd3..255b2c5a220d8 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -345,3 +345,39 @@ fn main() { let _ = &mut ({ *x.u }).x; } } + +mod issue_12969 { + use std::ops::Deref; + + struct Wrapper(T); + + impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } + } + + fn foo(_bar: &str) {} + + fn bar() { + let wrapped_bar = Wrapper(""); + + foo(&wrapped_bar); + } +} + +mod issue_9841 { + fn takes_array_ref(array: &&[T; N]) { + takes_slice(*array) + } + + fn takes_array_ref_ref(array: &&&[T; N]) { + takes_slice(**array) + } + + fn takes_slice(slice: &[T]) { + todo!() + } +} diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index 7531e1f87b71b..99906999f01d9 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -345,3 +345,39 @@ fn main() { let _ = &mut ({ *x.u }).x; } } + +mod issue_12969 { + use std::ops::Deref; + + struct Wrapper(T); + + impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } + } + + fn foo(_bar: &str) {} + + fn bar() { + let wrapped_bar = Wrapper(""); + + foo(&*wrapped_bar); + } +} + +mod issue_9841 { + fn takes_array_ref(array: &&[T; N]) { + takes_slice(*array) + } + + fn takes_array_ref_ref(array: &&&[T; N]) { + takes_slice(**array) + } + + fn takes_slice(slice: &[T]) { + todo!() + } +} diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index 56a183de3487f..53784934f6386 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -271,5 +271,11 @@ error: deref which would be done by auto-deref LL | let _ = &mut (*{ x.u }).x; | ^^^^^^^^^^ help: try: `{ x.u }` -error: aborting due to 45 previous errors +error: deref which would be done by auto-deref + --> tests/ui/explicit_auto_deref.rs:367:13 + | +LL | foo(&*wrapped_bar); + | ^^^^^^^^^^^^^ help: try: `&wrapped_bar` + +error: aborting due to 46 previous errors diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 1923ad7c677e0..78dd2c6c01c3d 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -71,19 +71,16 @@ fn main() { twice(ONE) != ONE; ONE as f64 != 2.0; //~^ ERROR: strict comparison of `f32` or `f64` - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` ONE as f64 != 0.0; // no error, comparison with zero is ok let x: f64 = 1.0; x == 1.0; //~^ ERROR: strict comparison of `f32` or `f64` - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` x != 0f64; // no error, comparison with zero is ok twice(x) != twice(ONE as f64); //~^ ERROR: strict comparison of `f32` or `f64` - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` x < 0.0; // no errors, lower or greater comparisons need no fuzzyness x > 0.0; @@ -105,17 +102,14 @@ fn main() { ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; //~^ ERROR: strict comparison of `f32` or `f64` - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` let a1: [f32; 1] = [0.0]; let a2: [f32; 1] = [1.1]; a1 == a2; //~^ ERROR: strict comparison of `f32` or `f64` arrays - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` a1[0] == a2[0]; //~^ ERROR: strict comparison of `f32` or `f64` - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` // no errors - comparing signums is ok let x32 = 3.21f32; diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index c8a0bde6e63a8..d10da8a99a9d8 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -4,49 +4,38 @@ error: strict comparison of `f32` or `f64` LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` = note: `-D clippy::float-cmp` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:79:5 + --> tests/ui/float_cmp.rs:78:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:84:5 + --> tests/ui/float_cmp.rs:82:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:106:5 + --> tests/ui/float_cmp.rs:103:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> tests/ui/float_cmp.rs:113:5 + --> tests/ui/float_cmp.rs:109:5 | LL | a1 == a2; | ^^^^^^^^ - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:116:5 + --> tests/ui/float_cmp.rs:111:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 6 previous errors diff --git a/tests/ui/float_cmp_const.rs b/tests/ui/float_cmp_const.rs index 47ea0e19c68b9..0818055643734 100644 --- a/tests/ui/float_cmp_const.rs +++ b/tests/ui/float_cmp_const.rs @@ -15,28 +15,21 @@ fn main() { // has errors 1f32 == ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` TWO == ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` TWO != ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` ONE + ONE == TWO; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` let x = 1; x as f32 == ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` let v = 0.9; v == ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` v != ONE; //~^ ERROR: strict comparison of `f32` or `f64` constant - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` // no errors, lower than or greater than comparisons v < ONE; @@ -70,5 +63,4 @@ fn main() { // has errors NON_ZERO_ARRAY == NON_ZERO_ARRAY2; //~^ ERROR: strict comparison of `f32` or `f64` constant arrays - //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` } diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index bffd2acc2e049..4f88746e958e2 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -4,65 +4,50 @@ error: strict comparison of `f32` or `f64` constant LL | 1f32 == ONE; | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` = note: `-D clippy::float-cmp-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::float_cmp_const)]` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:19:5 + --> tests/ui/float_cmp_const.rs:18:5 | LL | TWO == ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:22:5 + --> tests/ui/float_cmp_const.rs:20:5 | LL | TWO != ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:25:5 + --> tests/ui/float_cmp_const.rs:22:5 | LL | ONE + ONE == TWO; | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:29:5 + --> tests/ui/float_cmp_const.rs:25:5 | LL | x as f32 == ONE; | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:34:5 + --> tests/ui/float_cmp_const.rs:29:5 | LL | v == ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:37:5 + --> tests/ui/float_cmp_const.rs:31:5 | LL | v != ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays - --> tests/ui/float_cmp_const.rs:71:5 + --> tests/ui/float_cmp_const.rs:64:5 | LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 8 previous errors diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/init_numbered_fields.fixed similarity index 82% rename from tests/ui/numbered_fields.fixed rename to tests/ui/init_numbered_fields.fixed index 108520eed38d5..dca4e8da4d2f7 100644 --- a/tests/ui/numbered_fields.fixed +++ b/tests/ui/init_numbered_fields.fixed @@ -39,4 +39,13 @@ fn main() { struct TupleStructVec(Vec); let _ = TupleStructVec(vec![0, 1, 2, 3]); + + { + struct S(i32, i32); + let mut iter = [1i32, 1i32].into_iter(); + let _ = S { + 1: iter.next().unwrap(), + 0: iter.next().unwrap(), + }; + } } diff --git a/tests/ui/numbered_fields.rs b/tests/ui/init_numbered_fields.rs similarity index 84% rename from tests/ui/numbered_fields.rs rename to tests/ui/init_numbered_fields.rs index c718661a68263..8cb34705b4f67 100644 --- a/tests/ui/numbered_fields.rs +++ b/tests/ui/init_numbered_fields.rs @@ -47,4 +47,13 @@ fn main() { struct TupleStructVec(Vec); let _ = TupleStructVec { 0: vec![0, 1, 2, 3] }; + + { + struct S(i32, i32); + let mut iter = [1i32, 1i32].into_iter(); + let _ = S { + 1: iter.next().unwrap(), + 0: iter.next().unwrap(), + }; + } } diff --git a/tests/ui/numbered_fields.stderr b/tests/ui/init_numbered_fields.stderr similarity index 63% rename from tests/ui/numbered_fields.stderr rename to tests/ui/init_numbered_fields.stderr index 9d3f59cd3769e..f176e0c2ff301 100644 --- a/tests/ui/numbered_fields.stderr +++ b/tests/ui/init_numbered_fields.stderr @@ -1,5 +1,5 @@ error: used a field initializer for a tuple struct - --> tests/ui/numbered_fields.rs:17:13 + --> tests/ui/init_numbered_fields.rs:17:13 | LL | let _ = TupleStruct { | _____________^ @@ -7,13 +7,13 @@ LL | | 0: 1u32, LL | | 1: 42, LL | | 2: 23u8, LL | | }; - | |_____^ help: try: `TupleStruct(1u32, 42, 23u8)` + | |_____^ help: use tuple initialization: `TupleStruct(1u32, 42, 23u8)` | = note: `-D clippy::init-numbered-fields` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::init_numbered_fields)]` error: used a field initializer for a tuple struct - --> tests/ui/numbered_fields.rs:24:13 + --> tests/ui/init_numbered_fields.rs:24:13 | LL | let _ = TupleStruct { | _____________^ @@ -21,13 +21,13 @@ LL | | 0: 1u32, LL | | 2: 2u8, LL | | 1: 3u32, LL | | }; - | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)` + | |_____^ help: use tuple initialization: `TupleStruct(1u32, 3u32, 2u8)` error: used a field initializer for a tuple struct - --> tests/ui/numbered_fields.rs:49:13 + --> tests/ui/init_numbered_fields.rs:49:13 | LL | let _ = TupleStructVec { 0: vec![0, 1, 2, 3] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use tuple initialization: `TupleStructVec(vec![0, 1, 2, 3])` error: aborting due to 3 previous errors diff --git a/tests/ui/into_iter_without_iter.rs b/tests/ui/into_iter_without_iter.rs index c8b9076041a31..109259d6975c7 100644 --- a/tests/ui/into_iter_without_iter.rs +++ b/tests/ui/into_iter_without_iter.rs @@ -185,3 +185,42 @@ pub mod issue11635 { } } } + +pub mod issue12964 { + pub struct MyIter<'a, T: 'a> { + iter: std::slice::Iter<'a, T>, + } + + impl<'a, T> Iterator for MyIter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.iter.next() + } + } + + pub struct MyContainer { + inner: Vec, + } + + impl MyContainer {} + + impl MyContainer { + #[must_use] + pub fn iter(&self) -> MyIter<'_, T> { + <&Self as IntoIterator>::into_iter(self) + } + } + + impl<'a, T> IntoIterator for &'a MyContainer { + type Item = &'a T; + + type IntoIter = MyIter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter { + iter: self.inner.as_slice().iter(), + } + } + } +} diff --git a/tests/ui/iter_next_loop.rs b/tests/ui/iter_next_loop.rs index 548b799de44e8..d425f4da0e81d 100644 --- a/tests/ui/iter_next_loop.rs +++ b/tests/ui/iter_next_loop.rs @@ -3,7 +3,7 @@ fn main() { let x = [1, 2, 3, 4]; - for _ in vec.iter().next() {} + for _ in x.iter().next() {} struct Unrelated(&'static [u8]); impl Unrelated { diff --git a/tests/ui/iter_next_loop.stderr b/tests/ui/iter_next_loop.stderr index 85c23f4e70944..acc55031c3b29 100644 --- a/tests/ui/iter_next_loop.stderr +++ b/tests/ui/iter_next_loop.stderr @@ -1,9 +1,11 @@ -error[E0423]: expected value, found macro `vec` +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want --> tests/ui/iter_next_loop.rs:6:14 | -LL | for _ in vec.iter().next() {} - | ^^^ not a value +LL | for _ in x.iter().next() {} + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-next-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::iter_next_loop)]` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/manual_inspect.stderr b/tests/ui/manual_inspect.stderr index 8548c0cd29424..0559b3bd6611d 100644 --- a/tests/ui/manual_inspect.stderr +++ b/tests/ui/manual_inspect.stderr @@ -1,4 +1,4 @@ -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:5:21 | LL | let _ = Some(0).map(|x| { @@ -12,7 +12,7 @@ LL ~ let _ = Some(0).inspect(|&x| { LL ~ println!("{}", x); | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:10:21 | LL | let _ = Some(0).map(|x| { @@ -24,7 +24,7 @@ LL ~ let _ = Some(0).inspect(|&x| { LL ~ println!("{x}"); | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:15:21 | LL | let _ = Some(0).map(|x| { @@ -36,7 +36,7 @@ LL ~ let _ = Some(0).inspect(|&x| { LL ~ println!("{}", x * 5 + 1); | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:20:21 | LL | let _ = Some(0).map(|x| { @@ -50,7 +50,7 @@ LL | panic!(); LL ~ } | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:27:21 | LL | let _ = Some(0).map(|x| { @@ -65,7 +65,7 @@ LL | panic!(); LL ~ } | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:78:41 | LL | let _ = Some((String::new(), 0u32)).map(|x| { @@ -80,7 +80,7 @@ LL | panic!(); LL ~ } | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:104:33 | LL | let _ = Some(String::new()).map(|x| { @@ -98,7 +98,7 @@ LL | } LL ~ println!("test"); | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:115:21 | LL | let _ = Some(0).map(|x| { @@ -113,7 +113,7 @@ LL | panic!(); LL ~ } | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:130:46 | LL | let _ = Some(Cell2(Cell::new(0u32))).map(|x| { @@ -125,7 +125,7 @@ LL ~ let _ = Some(Cell2(Cell::new(0u32))).inspect(|x| { LL ~ x.0.set(1); | -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:146:34 | LL | let _: Result<_, ()> = Ok(0).map(|x| { @@ -137,7 +137,7 @@ LL ~ let _: Result<_, ()> = Ok(0).inspect(|&x| { LL ~ println!("{}", x); | -error: +error: using `map_err` over `inspect_err` --> tests/ui/manual_inspect.rs:151:35 | LL | let _: Result<(), _> = Err(0).map_err(|x| { @@ -166,7 +166,7 @@ LL | | .count(); = note: `-D clippy::suspicious-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]` -error: +error: using `map` over `inspect` --> tests/ui/manual_inspect.rs:158:10 | LL | .map(|x| { diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed new file mode 100644 index 0000000000000..5d33838a31877 --- /dev/null +++ b/tests/ui/manual_rotate.fixed @@ -0,0 +1,31 @@ +#![warn(clippy::manual_rotate)] +#![allow(unused)] +fn main() { + let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); + let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); + let a_u32 = 1u32; + // True positives + let y_u8 = x_u8.rotate_right(3); + let y_u16 = x_u16.rotate_right(7); + let y_u32 = x_u32.rotate_right(8); + let y_u64 = x_u64.rotate_right(9); + let y_i8 = x_i8.rotate_right(3); + let y_i16 = x_i16.rotate_right(7); + let y_i32 = x_i32.rotate_right(8); + let y_i64 = x_i64.rotate_right(9); + // Plus also works instead of | + let y_u32_plus = x_u32.rotate_right(8); + // Complex expression + let y_u32_complex = (x_u32 | 3256).rotate_right(8); + let y_u64_as = (x_u32 as u64).rotate_right(8); + + // False positives - can't be replaced with a rotation + let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); + let y_u32_false = (x_u32 >> 8) | (x_u32 >> 24); + let y_u64_false2 = (x_u64 >> 9) & (x_u64 << 55); + // Variable mismatch + let y_u32_wrong_vars = (x_u32 >> 8) | (a_u32 << 24); + // Has side effects and therefore should not be matched + let mut l = vec![12_u8, 34]; + let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); +} diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs new file mode 100644 index 0000000000000..5377491fb1a38 --- /dev/null +++ b/tests/ui/manual_rotate.rs @@ -0,0 +1,31 @@ +#![warn(clippy::manual_rotate)] +#![allow(unused)] +fn main() { + let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); + let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); + let a_u32 = 1u32; + // True positives + let y_u8 = (x_u8 >> 3) | (x_u8 << 5); + let y_u16 = (x_u16 >> 7) | (x_u16 << 9); + let y_u32 = (x_u32 >> 8) | (x_u32 << 24); + let y_u64 = (x_u64 >> 9) | (x_u64 << 55); + let y_i8 = (x_i8 >> 3) | (x_i8 << 5); + let y_i16 = (x_i16 >> 7) | (x_i16 << 9); + let y_i32 = (x_i32 >> 8) | (x_i32 << 24); + let y_i64 = (x_i64 >> 9) | (x_i64 << 55); + // Plus also works instead of | + let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); + // Complex expression + let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); + let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); + + // False positives - can't be replaced with a rotation + let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); + let y_u32_false = (x_u32 >> 8) | (x_u32 >> 24); + let y_u64_false2 = (x_u64 >> 9) & (x_u64 << 55); + // Variable mismatch + let y_u32_wrong_vars = (x_u32 >> 8) | (a_u32 << 24); + // Has side effects and therefore should not be matched + let mut l = vec![12_u8, 34]; + let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); +} diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr new file mode 100644 index 0000000000000..52da0861f7003 --- /dev/null +++ b/tests/ui/manual_rotate.stderr @@ -0,0 +1,71 @@ +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:8:16 + | +LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)` + | + = note: `-D clippy::manual-rotate` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:9:17 + | +LL | let y_u16 = (x_u16 >> 7) | (x_u16 << 9); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:10:17 + | +LL | let y_u32 = (x_u32 >> 8) | (x_u32 << 24); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:11:17 + | +LL | let y_u64 = (x_u64 >> 9) | (x_u64 << 55); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:12:16 + | +LL | let y_i8 = (x_i8 >> 3) | (x_i8 << 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:13:17 + | +LL | let y_i16 = (x_i16 >> 7) | (x_i16 << 9); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:14:17 + | +LL | let y_i32 = (x_i32 >> 8) | (x_i32 << 24); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:15:17 + | +LL | let y_i64 = (x_i64 >> 9) | (x_i64 << 55); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:17:22 + | +LL | let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:19:25 + | +LL | let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:20:20 + | +LL | let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index 2750e0cdf3f70..2c6e1e92da025 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -7,6 +7,7 @@ #![warn(clippy::missing_const_for_fn)] #![feature(start)] +#![feature(type_alias_impl_trait)] extern crate helper; extern crate proc_macros; @@ -180,4 +181,21 @@ mod msrv { unsafe { *self.1 as usize } } } + + #[clippy::msrv = "1.61"] + extern "C" fn c() {} +} + +mod with_extern { + extern "C-unwind" fn c_unwind() {} + extern "system" fn system() {} + extern "system-unwind" fn system_unwind() {} +} + +mod with_ty_alias { + type Foo = impl std::fmt::Debug; + + fn foo(_: Foo) { + let _: Foo = 1; + } } diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index f8fc935f3679d..dbd739eee1389 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -143,6 +143,21 @@ mod msrv { let bar = Bar { val: 1 }; let _ = unsafe { bar.val }; } + + #[clippy::msrv = "1.62"] + mod with_extern { + const extern "C" fn c() {} + //~^ ERROR: this could be a `const fn` + + #[rustfmt::skip] + const extern fn implicit_c() {} + //~^ ERROR: this could be a `const fn` + + // any item functions in extern block won't trigger this lint + extern "C" { + fn c_in_block(); + } + } } mod issue12677 { @@ -174,3 +189,18 @@ mod issue12677 { } } } + +mod with_ty_alias { + trait FooTrait { + type Foo: std::fmt::Debug; + fn bar(_: Self::Foo) {} + } + impl FooTrait for () { + type Foo = i32; + } + // NOTE: When checking the type of a function param, make sure it is not an alias with + // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type + // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated + // in this test. + const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 5e4e2c58e5a4d..4ac56f4c80348 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -143,6 +143,21 @@ mod msrv { let bar = Bar { val: 1 }; let _ = unsafe { bar.val }; } + + #[clippy::msrv = "1.62"] + mod with_extern { + extern "C" fn c() {} + //~^ ERROR: this could be a `const fn` + + #[rustfmt::skip] + extern fn implicit_c() {} + //~^ ERROR: this could be a `const fn` + + // any item functions in extern block won't trigger this lint + extern "C" { + fn c_in_block(); + } + } } mod issue12677 { @@ -174,3 +189,18 @@ mod issue12677 { } } } + +mod with_ty_alias { + trait FooTrait { + type Foo: std::fmt::Debug; + fn bar(_: Self::Foo) {} + } + impl FooTrait for () { + type Foo = i32; + } + // NOTE: When checking the type of a function param, make sure it is not an alias with + // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type + // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated + // in this test. + fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 8302b074127e6..fb4db70310316 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -211,7 +211,29 @@ LL | const fn union_access_can_be_const() { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:155:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:149:9 + | +LL | extern "C" fn c() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | const extern "C" fn c() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9 + | +LL | extern fn implicit_c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | const extern fn implicit_c() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9 | LL | / pub fn new(strings: Vec) -> Self { LL | | Self { strings } @@ -224,7 +246,7 @@ LL | pub const fn new(strings: Vec) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:160:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9 | LL | / pub fn empty() -> Self { LL | | Self { strings: Vec::new() } @@ -237,7 +259,7 @@ LL | pub const fn empty() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:171:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9 | LL | / pub fn new(text: String) -> Self { LL | | let vec = Vec::new(); @@ -250,5 +272,16 @@ help: make the function `const` LL | pub const fn new(text: String) -> Self { | +++++ -error: aborting due to 18 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5 + | +LL | fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} + | +++++ + +error: aborting due to 21 previous errors diff --git a/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed new file mode 100644 index 0000000000000..c103db536ab50 --- /dev/null +++ b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed @@ -0,0 +1,14 @@ +#![warn(clippy::missing_const_for_fn)] +#![allow(unsupported_calling_conventions)] +#![feature(const_extern_fn)] + +const extern "C-unwind" fn c_unwind() {} +//~^ ERROR: this could be a `const fn` +const extern "system" fn system() {} +//~^ ERROR: this could be a `const fn` +const extern "system-unwind" fn system_unwind() {} +//~^ ERROR: this could be a `const fn` +pub const extern "stdcall" fn std_call() {} +//~^ ERROR: this could be a `const fn` +pub const extern "stdcall-unwind" fn std_call_unwind() {} +//~^ ERROR: this could be a `const fn` diff --git a/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs new file mode 100644 index 0000000000000..0f7020ae559a9 --- /dev/null +++ b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs @@ -0,0 +1,14 @@ +#![warn(clippy::missing_const_for_fn)] +#![allow(unsupported_calling_conventions)] +#![feature(const_extern_fn)] + +extern "C-unwind" fn c_unwind() {} +//~^ ERROR: this could be a `const fn` +extern "system" fn system() {} +//~^ ERROR: this could be a `const fn` +extern "system-unwind" fn system_unwind() {} +//~^ ERROR: this could be a `const fn` +pub extern "stdcall" fn std_call() {} +//~^ ERROR: this could be a `const fn` +pub extern "stdcall-unwind" fn std_call_unwind() {} +//~^ ERROR: this could be a `const fn` diff --git a/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr new file mode 100644 index 0000000000000..036094a367b22 --- /dev/null +++ b/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr @@ -0,0 +1,59 @@ +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:5:1 + | +LL | extern "C-unwind" fn c_unwind() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]` +help: make the function `const` + | +LL | const extern "C-unwind" fn c_unwind() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:7:1 + | +LL | extern "system" fn system() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | const extern "system" fn system() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:9:1 + | +LL | extern "system-unwind" fn system_unwind() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | const extern "system-unwind" fn system_unwind() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:11:1 + | +LL | pub extern "stdcall" fn std_call() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | pub const extern "stdcall" fn std_call() {} + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:13:1 + | +LL | pub extern "stdcall-unwind" fn std_call_unwind() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `const` + | +LL | pub const extern "stdcall-unwind" fn std_call_unwind() {} + | +++++ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/thread_local_initializer_can_be_made_const.fixed b/tests/ui/missing_const_for_thread_local.fixed similarity index 97% rename from tests/ui/thread_local_initializer_can_be_made_const.fixed rename to tests/ui/missing_const_for_thread_local.fixed index 4c9bd0bd8634f..90b31b9b5d219 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.fixed +++ b/tests/ui/missing_const_for_thread_local.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::thread_local_initializer_can_be_made_const)] +#![warn(clippy::missing_const_for_thread_local)] use std::cell::{Cell, RefCell}; diff --git a/tests/ui/thread_local_initializer_can_be_made_const.rs b/tests/ui/missing_const_for_thread_local.rs similarity index 97% rename from tests/ui/thread_local_initializer_can_be_made_const.rs rename to tests/ui/missing_const_for_thread_local.rs index eb336f0dd1913..f97e4848fd7a6 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.rs +++ b/tests/ui/missing_const_for_thread_local.rs @@ -1,4 +1,4 @@ -#![warn(clippy::thread_local_initializer_can_be_made_const)] +#![warn(clippy::missing_const_for_thread_local)] use std::cell::{Cell, RefCell}; diff --git a/tests/ui/thread_local_initializer_can_be_made_const.stderr b/tests/ui/missing_const_for_thread_local.stderr similarity index 72% rename from tests/ui/thread_local_initializer_can_be_made_const.stderr rename to tests/ui/missing_const_for_thread_local.stderr index b4f8bd822b0de..c143a37454f5b 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.stderr +++ b/tests/ui/missing_const_for_thread_local.stderr @@ -1,38 +1,38 @@ error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:8:41 + --> tests/ui/missing_const_for_thread_local.rs:8:41 | LL | static BUF_1: RefCell = RefCell::new(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` | - = note: `-D clippy::thread-local-initializer-can-be-made-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::thread_local_initializer_can_be_made_const)]` + = note: `-D clippy::missing-const-for-thread-local` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_thread_local)]` error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:18:29 + --> tests/ui/missing_const_for_thread_local.rs:18:29 | LL | static SIMPLE:i32 = 1; | ^ help: replace with: `const { 1 }` error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:24:59 + --> tests/ui/missing_const_for_thread_local.rs:24:59 | LL | static BUF_3_CAN_BE_MADE_CONST: RefCell = RefCell::new(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:26:59 + --> tests/ui/missing_const_for_thread_local.rs:26:59 | LL | static BUF_4_CAN_BE_MADE_CONST: RefCell = RefCell::new(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:32:31 + --> tests/ui/missing_const_for_thread_local.rs:32:31 | LL | static PEEL_ME: i32 = { 1 }; | ^^^^^ help: replace with: `const { 1 }` error: initializer for `thread_local` value can be made `const` - --> tests/ui/thread_local_initializer_can_be_made_const.rs:34:36 + --> tests/ui/missing_const_for_thread_local.rs:34:36 | LL | static PEEL_ME_MANY: i32 = { let x = 1; x * x }; | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { { let x = 1; x * x } }` diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index eee62122fdfda..162ec82aeded2 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -232,43 +232,48 @@ async fn async_vec2(b: &mut Vec) { } fn non_mut(n: &str) {} //Should warn -pub async fn call_in_closure1(n: &mut str) { +async fn call_in_closure1(n: &mut str) { (|| non_mut(n))() } fn str_mut(str: &mut String) -> bool { str.pop().is_some() } //Should not warn -pub async fn call_in_closure2(str: &mut String) { +async fn call_in_closure2(str: &mut String) { (|| str_mut(str))(); } // Should not warn. -pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { +async fn closure(n: &mut usize) -> impl '_ + FnMut() { || { *n += 1; } } // Should warn. -pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { +fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { //~^ ERROR: this argument is a mutable reference, but not used mutably || *n + 1 } // Should not warn. -pub async fn closure3(n: &mut usize) { +async fn closure3(n: &mut usize) { (|| *n += 1)(); } // Should warn. -pub async fn closure4(n: &mut usize) { +async fn closure4(n: &mut usize) { //~^ ERROR: this argument is a mutable reference, but not used mutably (|| { let _x = *n + 1; })(); } +// Should not warn: pub +pub fn pub_foo(s: &mut Vec, b: &u32, x: &mut u32) { + *x += *b + s.len() as u32; +} + // Should not warn. async fn _f(v: &mut Vec<()>) { let x = || v.pop(); @@ -365,4 +370,5 @@ fn main() { used_as_path; let _: fn(&mut u32) = passed_as_local; let _ = if v[0] == 0 { ty_unify_1 } else { ty_unify_2 }; + pub_foo(&mut v, &0, &mut u); } diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 51e3ba37dede1..f462fa9099ed6 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -108,109 +108,103 @@ LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:235:34 + --> tests/ui/needless_pass_by_ref_mut.rs:235:30 | -LL | pub async fn call_in_closure1(n: &mut str) { - | ^^^^^^^^ help: consider changing to: `&str` - | - = warning: changing this function will impact semver compatibility +LL | async fn call_in_closure1(n: &mut str) { + | ^^^^^^^^ help: consider changing to: `&str` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:254:20 - | -LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { - | ^^^^^^^^^^ help: consider changing to: `&usize` + --> tests/ui/needless_pass_by_ref_mut.rs:254:16 | - = warning: changing this function will impact semver compatibility +LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:265:26 - | -LL | pub async fn closure4(n: &mut usize) { - | ^^^^^^^^^^ help: consider changing to: `&usize` + --> tests/ui/needless_pass_by_ref_mut.rs:265:22 | - = warning: changing this function will impact semver compatibility +LL | async fn closure4(n: &mut usize) { + | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:314:12 + --> tests/ui/needless_pass_by_ref_mut.rs:319:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:316:18 + --> tests/ui/needless_pass_by_ref_mut.rs:321:18 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:316:45 + --> tests/ui/needless_pass_by_ref_mut.rs:321:45 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:324:46 + --> tests/ui/needless_pass_by_ref_mut.rs:329:46 | LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:340:18 + --> tests/ui/needless_pass_by_ref_mut.rs:345:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:341:19 + --> tests/ui/needless_pass_by_ref_mut.rs:346:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:342:18 + --> tests/ui/needless_pass_by_ref_mut.rs:347:18 | LL | fn _multi_tup(x: &mut ((i32, u32))) {} | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:343:11 + --> tests/ui/needless_pass_by_ref_mut.rs:348:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:345:23 + --> tests/ui/needless_pass_by_ref_mut.rs:350:23 | LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:346:20 + --> tests/ui/needless_pass_by_ref_mut.rs:351:20 | LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:347:18 + --> tests/ui/needless_pass_by_ref_mut.rs:352:18 | LL | fn _unsafe_fn(x: &mut unsafe fn()) {} | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:348:25 + --> tests/ui/needless_pass_by_ref_mut.rs:353:25 | LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:349:20 + --> tests/ui/needless_pass_by_ref_mut.rs:354:20 | LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:350:20 + --> tests/ui/needless_pass_by_ref_mut.rs:355:20 | LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` diff --git a/tests/ui/needless_pass_by_ref_mut2.fixed b/tests/ui/needless_pass_by_ref_mut2.fixed index 3c2576213cd77..f26b39ea6a16b 100644 --- a/tests/ui/needless_pass_by_ref_mut2.fixed +++ b/tests/ui/needless_pass_by_ref_mut2.fixed @@ -5,7 +5,7 @@ #![allow(clippy::redundant_closure_call)] #![warn(clippy::needless_pass_by_ref_mut)] -pub async fn inner_async3(x: &i32, y: &mut u32) { +async fn inner_async3(x: &i32, y: &mut u32) { //~^ ERROR: this argument is a mutable reference, but not used mutably async { *y += 1; @@ -13,7 +13,7 @@ pub async fn inner_async3(x: &i32, y: &mut u32) { .await; } -pub async fn inner_async4(u: &mut i32, v: &u32) { +async fn inner_async4(u: &mut i32, v: &u32) { //~^ ERROR: this argument is a mutable reference, but not used mutably async { *u += 1; diff --git a/tests/ui/needless_pass_by_ref_mut2.rs b/tests/ui/needless_pass_by_ref_mut2.rs index 34b0b564deb8e..4220215b1fe9b 100644 --- a/tests/ui/needless_pass_by_ref_mut2.rs +++ b/tests/ui/needless_pass_by_ref_mut2.rs @@ -5,7 +5,7 @@ #![allow(clippy::redundant_closure_call)] #![warn(clippy::needless_pass_by_ref_mut)] -pub async fn inner_async3(x: &mut i32, y: &mut u32) { +async fn inner_async3(x: &mut i32, y: &mut u32) { //~^ ERROR: this argument is a mutable reference, but not used mutably async { *y += 1; @@ -13,7 +13,7 @@ pub async fn inner_async3(x: &mut i32, y: &mut u32) { .await; } -pub async fn inner_async4(u: &mut i32, v: &mut u32) { +async fn inner_async4(u: &mut i32, v: &mut u32) { //~^ ERROR: this argument is a mutable reference, but not used mutably async { *u += 1; diff --git a/tests/ui/needless_pass_by_ref_mut2.stderr b/tests/ui/needless_pass_by_ref_mut2.stderr index c875360322568..1c0136cf5d594 100644 --- a/tests/ui/needless_pass_by_ref_mut2.stderr +++ b/tests/ui/needless_pass_by_ref_mut2.stderr @@ -1,20 +1,17 @@ error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut2.rs:8:30 + --> tests/ui/needless_pass_by_ref_mut2.rs:8:26 | -LL | pub async fn inner_async3(x: &mut i32, y: &mut u32) { - | ^^^^^^^^ help: consider changing to: `&i32` +LL | async fn inner_async3(x: &mut i32, y: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&i32` | - = warning: changing this function will impact semver compatibility = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut2.rs:16:43 + --> tests/ui/needless_pass_by_ref_mut2.rs:16:39 | -LL | pub async fn inner_async4(u: &mut i32, v: &mut u32) { - | ^^^^^^^^ help: consider changing to: `&u32` - | - = warning: changing this function will impact semver compatibility +LL | async fn inner_async4(u: &mut i32, v: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&u32` error: aborting due to 2 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 853f685f04c76..fc4129e1db84f 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -228,12 +228,41 @@ fn needless_return_macro() -> String { format!("Hello {}", "world!") } -fn issue_9361() -> i32 { - let n = 1; - #[allow(clippy::arithmetic_side_effects)] +fn issue_9361(n: i32) -> i32 { + #[expect(clippy::arithmetic_side_effects)] return n + n; } +mod issue_12998 { + fn expect_lint() -> i32 { + let x = 1; + + #[expect(clippy::needless_return)] + return x; + } + + fn expect_group() -> i32 { + let x = 1; + + #[expect(clippy::style)] + return x; + } + + fn expect_all() -> i32 { + let x = 1; + + #[expect(clippy::all)] + return x; + } + + fn expect_warnings() -> i32 { + let x = 1; + + #[expect(warnings)] + return x; + } +} + fn issue8336(x: i32) -> bool { if x > 0 { println!("something"); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index e9c1e0e8ae8ed..61c7a02008f08 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -236,12 +236,41 @@ fn needless_return_macro() -> String { return format!("Hello {}", "world!"); } -fn issue_9361() -> i32 { - let n = 1; - #[allow(clippy::arithmetic_side_effects)] +fn issue_9361(n: i32) -> i32 { + #[expect(clippy::arithmetic_side_effects)] return n + n; } +mod issue_12998 { + fn expect_lint() -> i32 { + let x = 1; + + #[expect(clippy::needless_return)] + return x; + } + + fn expect_group() -> i32 { + let x = 1; + + #[expect(clippy::style)] + return x; + } + + fn expect_all() -> i32 { + let x = 1; + + #[expect(clippy::all)] + return x; + } + + fn expect_warnings() -> i32 { + let x = 1; + + #[expect(warnings)] + return x; + } +} + fn issue8336(x: i32) -> bool { if x > 0 { println!("something"); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 6c891fe7ad3f8..ea9c230eafd22 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -483,7 +483,7 @@ LL + format!("Hello {}", "world!") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:248:9 + --> tests/ui/needless_return.rs:277:9 | LL | return true; | ^^^^^^^^^^^ @@ -497,7 +497,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:250:9 + --> tests/ui/needless_return.rs:279:9 | LL | return false; | ^^^^^^^^^^^^ @@ -509,7 +509,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:257:13 + --> tests/ui/needless_return.rs:286:13 | LL | return 10; | ^^^^^^^^^ @@ -524,7 +524,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:260:13 + --> tests/ui/needless_return.rs:289:13 | LL | return 100; | ^^^^^^^^^^ @@ -537,7 +537,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:268:9 + --> tests/ui/needless_return.rs:297:9 | LL | return 0; | ^^^^^^^^ @@ -549,7 +549,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:275:13 + --> tests/ui/needless_return.rs:304:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -564,7 +564,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:277:13 + --> tests/ui/needless_return.rs:306:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -577,7 +577,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:284:20 + --> tests/ui/needless_return.rs:313:20 | LL | let _ = 42; | ____________________^ @@ -594,7 +594,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:291:20 + --> tests/ui/needless_return.rs:320:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -606,7 +606,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:303:9 + --> tests/ui/needless_return.rs:332:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -618,7 +618,7 @@ LL + Ok(format!("ok!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:305:9 + --> tests/ui/needless_return.rs:334:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -630,7 +630,7 @@ LL + Err(format!("err!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:311:9 + --> tests/ui/needless_return.rs:340:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -642,7 +642,7 @@ LL + if true { 1 } else { 2 } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:315:9 + --> tests/ui/needless_return.rs:344:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -654,7 +654,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else | error: unneeded `return` statement - --> tests/ui/needless_return.rs:336:5 + --> tests/ui/needless_return.rs:365:5 | LL | return { "a".to_string() } + "b" + { "c" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/overflow_check_conditional.rs b/tests/ui/overflow_check_conditional.rs deleted file mode 100644 index a70bb3bc47bfb..0000000000000 --- a/tests/ui/overflow_check_conditional.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![warn(clippy::overflow_check_conditional)] -#![allow(clippy::needless_if)] - -fn test(a: u32, b: u32, c: u32) { - if a + b < a {} - //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust - //~| NOTE: `-D clippy::overflow-check-conditional` implied by `-D warnings` - if a > a + b {} - //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust - if a + b < b {} - //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust - if b > a + b {} - //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust - if a - b > b {} - //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus - if b < a - b {} - //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus - if a - b > a {} - //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus - if a < a - b {} - //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus - if a + b < c {} - if c > a + b {} - if a - b < c {} - if c > a - b {} - let i = 1.1; - let j = 2.2; - if i + j < i {} - if i - j < i {} - if i > i + j {} - if i - j < i {} -} - -fn main() { - test(1, 2, 3) -} diff --git a/tests/ui/overflow_check_conditional.stderr b/tests/ui/overflow_check_conditional.stderr deleted file mode 100644 index c14532bad5ab7..0000000000000 --- a/tests/ui/overflow_check_conditional.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: you are trying to use classic C overflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:5:8 - | -LL | if a + b < a {} - | ^^^^^^^^^ - | - = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::overflow_check_conditional)]` - -error: you are trying to use classic C overflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:8:8 - | -LL | if a > a + b {} - | ^^^^^^^^^ - -error: you are trying to use classic C overflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:10:8 - | -LL | if a + b < b {} - | ^^^^^^^^^ - -error: you are trying to use classic C overflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:12:8 - | -LL | if b > a + b {} - | ^^^^^^^^^ - -error: you are trying to use classic C underflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:14:8 - | -LL | if a - b > b {} - | ^^^^^^^^^ - -error: you are trying to use classic C underflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:16:8 - | -LL | if b < a - b {} - | ^^^^^^^^^ - -error: you are trying to use classic C underflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:18:8 - | -LL | if a - b > a {} - | ^^^^^^^^^ - -error: you are trying to use classic C underflow conditions that will fail in Rust - --> tests/ui/overflow_check_conditional.rs:20:8 - | -LL | if a < a - b {} - | ^^^^^^^^^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/panicking_overflow_checks.rs b/tests/ui/panicking_overflow_checks.rs new file mode 100644 index 0000000000000..dc2ddeada1e94 --- /dev/null +++ b/tests/ui/panicking_overflow_checks.rs @@ -0,0 +1,27 @@ +#![warn(clippy::panicking_overflow_checks)] +#![allow(clippy::needless_if)] + +fn test(a: u32, b: u32, c: u32) { + if a + b < a {} //~ panicking_overflow_checks + if a > a + b {} //~ panicking_overflow_checks + if a + b < b {} //~ panicking_overflow_checks + if b > a + b {} //~ panicking_overflow_checks + if a - b > b {} + if b < a - b {} + if a - b > a {} //~ panicking_overflow_checks + if a < a - b {} //~ panicking_overflow_checks + if a + b < c {} + if c > a + b {} + if a - b < c {} + if c > a - b {} + let i = 1.1; + let j = 2.2; + if i + j < i {} + if i - j < i {} + if i > i + j {} + if i - j < i {} +} + +fn main() { + test(1, 2, 3) +} diff --git a/tests/ui/panicking_overflow_checks.stderr b/tests/ui/panicking_overflow_checks.stderr new file mode 100644 index 0000000000000..1fae045788996 --- /dev/null +++ b/tests/ui/panicking_overflow_checks.stderr @@ -0,0 +1,41 @@ +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:5:8 + | +LL | if a + b < a {} + | ^^^^^^^^^ + | + = note: `-D clippy::panicking-overflow-checks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panicking_overflow_checks)]` + +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:6:8 + | +LL | if a > a + b {} + | ^^^^^^^^^ + +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:7:8 + | +LL | if a + b < b {} + | ^^^^^^^^^ + +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:8:8 + | +LL | if b > a + b {} + | ^^^^^^^^^ + +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:11:8 + | +LL | if a - b > a {} + | ^^^^^^^^^ + +error: you are trying to use classic C overflow conditions that will fail in Rust + --> tests/ui/panicking_overflow_checks.rs:12:8 + | +LL | if a < a - b {} + | ^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index e162f35baf551..18462620b0a30 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,4 +1,4 @@ -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:18:33 | LL | *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T) } @@ -7,37 +7,37 @@ LL | *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::i = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:27:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:28:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:33:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:46:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:47:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:50:21 | LL | let _ = inline!($ptr as *const i32); @@ -45,157 +45,157 @@ LL | let _ = inline!($ptr as *const i32); | = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:71:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:72:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:79:9 | LL | ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:83:9 | LL | std::ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:88:9 | LL | ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:92:9 | LL | core::ptr::null_mut() as *mut u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:97:9 | LL | ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:101:9 | LL | std::ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:106:9 | LL | ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:110:9 | LL | core::ptr::null() as *const u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:117:9 | LL | ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:121:9 | LL | std::ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:126:9 | LL | ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:130:9 | LL | core::ptr::null_mut() as *mut _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:135:9 | LL | ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:139:9 | LL | std::ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:144:9 | LL | ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:148:9 | LL | core::ptr::null() as *const _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:155:9 | LL | ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:159:9 | LL | std::ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:164:9 | LL | ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:168:9 | LL | core::ptr::null_mut() as _ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:173:9 | LL | ptr::null() as _ | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:177:9 | LL | std::ptr::null() as _ | ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:182:9 | LL | ptr::null() as _ | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` -error: `as` casting between raw pointers without changing its mutability +error: `as` casting between raw pointers without changing their constness --> tests/ui/ptr_as_ptr.rs:186:9 | LL | core::ptr::null() as _ diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 24d0f79754289..d70c9f8d06c07 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -24,9 +24,11 @@ #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] +#![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] @@ -77,12 +79,14 @@ #![warn(clippy::map_unwrap_or)] #![warn(clippy::map_unwrap_or)] #![warn(clippy::unwrap_used)] +#![warn(clippy::panicking_overflow_checks)] #![warn(clippy::needless_borrow)] #![warn(clippy::expect_used)] #![warn(clippy::map_unwrap_or)] #![warn(clippy::unwrap_used)] #![warn(clippy::single_char_add_str)] #![warn(clippy::module_name_repetitions)] +#![warn(clippy::missing_const_for_thread_local)] #![warn(clippy::recursive_format_impl)] #![warn(clippy::unwrap_or_default)] #![warn(clippy::invisible_characters)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index be8da2fa1a38e..8d0ac3c8f9555 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -24,9 +24,11 @@ #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] +#![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] @@ -77,12 +79,14 @@ #![warn(clippy::option_map_unwrap_or)] #![warn(clippy::option_map_unwrap_or_else)] #![warn(clippy::option_unwrap_used)] +#![warn(clippy::overflow_check_conditional)] #![warn(clippy::ref_in_deref)] #![warn(clippy::result_expect_used)] #![warn(clippy::result_map_unwrap_or_else)] #![warn(clippy::result_unwrap_used)] #![warn(clippy::single_char_push_str)] #![warn(clippy::stutter)] +#![warn(clippy::thread_local_initializer_can_be_made_const)] #![warn(clippy::to_string_in_display)] #![warn(clippy::unwrap_or_else_default)] #![warn(clippy::zero_width_space)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 777ac20153da3..d6637324a030b 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:56:9 + --> tests/ui/rename.rs:58:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,346 +8,358 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:57:9 + --> tests/ui/rename.rs:59:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:58:9 + --> tests/ui/rename.rs:60:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:59:9 + --> tests/ui/rename.rs:61:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:60:9 + --> tests/ui/rename.rs:62:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:61:9 + --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:62:9 + --> tests/ui/rename.rs:64:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:63:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` + --> tests/ui/rename.rs:82:9 + | +LL | #![warn(clippy::overflow_check_conditional)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` + error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` + --> tests/ui/rename.rs:89:9 + | +LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` + error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 58 previous errors +error: aborting due to 60 previous errors diff --git a/tests/ui/set_contains_or_insert.rs b/tests/ui/set_contains_or_insert.rs new file mode 100644 index 0000000000000..8465007402ab0 --- /dev/null +++ b/tests/ui/set_contains_or_insert.rs @@ -0,0 +1,83 @@ +#![allow(unused)] +#![allow(clippy::nonminimal_bool)] +#![allow(clippy::needless_borrow)] +#![warn(clippy::set_contains_or_insert)] + +use std::collections::HashSet; + +fn main() { + should_warn_cases(); + + should_not_warn_cases(); +} + +fn should_warn_cases() { + let mut set = HashSet::new(); + let value = 5; + + if !set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if !set.contains(&value) { + set.insert(value); + } + + if !!set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if (&set).contains(&value) { + set.insert(value); + } + + let borrow_value = &6; + if !set.contains(borrow_value) { + set.insert(*borrow_value); + } + + let borrow_set = &mut set; + if !borrow_set.contains(&value) { + borrow_set.insert(value); + } +} + +fn should_not_warn_cases() { + let mut set = HashSet::new(); + let value = 5; + let another_value = 6; + + if !set.contains(&value) { + set.insert(another_value); + } + + if !set.contains(&value) { + println!("Just a comment"); + } + + if simply_true() { + set.insert(value); + } + + if !set.contains(&value) { + set.replace(value); //it is not insert + println!("Just a comment"); + } + + if set.contains(&value) { + println!("value is already in set"); + } else { + set.insert(value); + } +} + +fn simply_true() -> bool { + true +} diff --git a/tests/ui/set_contains_or_insert.stderr b/tests/ui/set_contains_or_insert.stderr new file mode 100644 index 0000000000000..507e20964fc20 --- /dev/null +++ b/tests/ui/set_contains_or_insert.stderr @@ -0,0 +1,61 @@ +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:18:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::set-contains-or-insert` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]` + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:23:12 + | +LL | if set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:28:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:32:14 + | +LL | if !!set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:37:15 + | +LL | if (&set).contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:42:13 + | +LL | if !set.contains(borrow_value) { + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | set.insert(*borrow_value); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `HashSet::insert` after `HashSet::contains` + --> tests/ui/set_contains_or_insert.rs:47:20 + | +LL | if !borrow_set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | borrow_set.insert(value); + | ^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 8ee15440ccf01..0db6fbfb7be9b 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -801,4 +801,30 @@ fn should_not_trigger_lint_with_explicit_drop() { } } +fn should_trigger_lint_in_if_let() { + let mutex = Mutex::new(vec![1]); + + if let Some(val) = mutex.lock().unwrap().first().copied() { + //~^ ERROR: temporary with significant `Drop` in `if let` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + println!("{}", val); + } + + // Should not trigger lint without the final `copied()`, because we actually hold a reference + // (i.e., the `val`) to the locked data. + if let Some(val) = mutex.lock().unwrap().first() { + println!("{}", val); + }; +} + +fn should_trigger_lint_in_while_let() { + let mutex = Mutex::new(vec![1]); + + while let Some(val) = mutex.lock().unwrap().pop() { + //~^ ERROR: temporary with significant `Drop` in `while let` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + println!("{}", val); + } +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 4a483e79d8add..c0c93cd10c027 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -541,5 +541,32 @@ LL ~ let value = mutex.lock().unwrap()[0]; LL ~ for val in [value, 2] { | -error: aborting due to 27 previous errors +error: temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression + --> tests/ui/significant_drop_in_scrutinee.rs:807:24 + | +LL | if let Some(val) = mutex.lock().unwrap().first().copied() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().first().copied(); +LL ~ if let Some(val) = value { + | + +error: temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression + --> tests/ui/significant_drop_in_scrutinee.rs:823:27 + | +LL | while let Some(val) = mutex.lock().unwrap().pop() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior + +error: aborting due to 29 previous errors diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 11761c6c90e36..006f123cbcdfc 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -43,7 +43,7 @@ fn get_number() -> i32 { 0 } -fn get_usize() -> usize { +const fn get_usize() -> usize { 0 } fn get_struct() -> Struct { @@ -113,4 +113,16 @@ fn main() { 'label: { break 'label }; + let () = const { + [42, 55][get_usize()]; + }; +} + +const _: () = { + [42, 55][get_usize()]; +}; + +const fn foo() { + assert!([42, 55].len() > get_usize()); + //~^ ERROR: unnecessary operation } diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index de0081289ac3d..b4067c7407415 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -43,7 +43,7 @@ fn get_number() -> i32 { 0 } -fn get_usize() -> usize { +const fn get_usize() -> usize { 0 } fn get_struct() -> Struct { @@ -117,4 +117,16 @@ fn main() { 'label: { break 'label }; + let () = const { + [42, 55][get_usize()]; + }; +} + +const _: () = { + [42, 55][get_usize()]; +}; + +const fn foo() { + [42, 55][get_usize()]; + //~^ ERROR: unnecessary operation } diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index 27be5e6f4b922..036a9a44bbad0 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -119,5 +119,11 @@ LL | | s: String::from("blah"), LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` -error: aborting due to 19 previous errors +error: unnecessary operation + --> tests/ui/unnecessary_operation.rs:130:5 + | +LL | [42, 55][get_usize()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` + +error: aborting due to 20 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 1afa5ab54c461..fdcac8fb08dcf 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -157,6 +157,25 @@ fn main() { require_path(&std::path::PathBuf::from("x")); require_str(&String::from("x")); require_slice(&[String::from("x")]); + + let slice = [0u8; 1024]; + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); + // Expression is of type `&String`, can't suggest `str::from_utf8` here + let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap(); + macro_rules! arg_from_macro { + () => { + b"foo".to_vec() + }; + } + macro_rules! string_from_utf8_from_macro { + () => { + &String::from_utf8(b"foo".to_vec()).unwrap() + }; + } + let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap(); + let _ref_str: &str = string_from_utf8_from_macro!(); } fn require_c_str(_: &CStr) {} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa88dde43bf66..10a9727a9a798 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -157,6 +157,25 @@ fn main() { require_path(&std::path::PathBuf::from("x").to_path_buf()); require_str(&String::from("x").to_string()); require_slice(&[String::from("x")].to_owned()); + + let slice = [0u8; 1024]; + let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); + let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); + let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); + // Expression is of type `&String`, can't suggest `str::from_utf8` here + let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap(); + macro_rules! arg_from_macro { + () => { + b"foo".to_vec() + }; + } + macro_rules! string_from_utf8_from_macro { + () => { + &String::from_utf8(b"foo".to_vec()).unwrap() + }; + } + let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap(); + let _ref_str: &str = string_from_utf8_from_macro!(); } fn require_c_str(_: &CStr) {} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 2829f3cd6e980..511b4ae119f8a 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -477,8 +477,44 @@ error: unnecessary use of `to_owned` LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` +error: allocating a new `String` only to create a temporary `&str` from it + --> tests/ui/unnecessary_to_owned.rs:162:26 + | +LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: convert from `&[u8]` to `&str` directly + | +LL - let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); +LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); + | + +error: allocating a new `String` only to create a temporary `&str` from it + --> tests/ui/unnecessary_to_owned.rs:163:26 + | +LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: convert from `&[u8]` to `&str` directly + | +LL - let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); +LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); + | + +error: allocating a new `String` only to create a temporary `&str` from it + --> tests/ui/unnecessary_to_owned.rs:164:26 + | +LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: convert from `&[u8]` to `&str` directly + | +LL - let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); +LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); + | + error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:202:14 + --> tests/ui/unnecessary_to_owned.rs:221:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -494,64 +530,64 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:225:14 + --> tests/ui/unnecessary_to_owned.rs:244:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:230:14 + --> tests/ui/unnecessary_to_owned.rs:249:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:278:24 + --> tests/ui/unnecessary_to_owned.rs:297:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:387:12 + --> tests/ui/unnecessary_to_owned.rs:406:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:530:37 + --> tests/ui/unnecessary_to_owned.rs:549:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:540:18 + --> tests/ui/unnecessary_to_owned.rs:559:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:544:14 + --> tests/ui/unnecessary_to_owned.rs:563:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:545:14 + --> tests/ui/unnecessary_to_owned.rs:564:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:550:14 + --> tests/ui/unnecessary_to_owned.rs:569:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:551:14 + --> tests/ui/unnecessary_to_owned.rs:570:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 85 previous errors +error: aborting due to 88 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 6fdd728b9b72e..46890ee9213f1 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -204,6 +204,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass { use super::*; @@ -212,6 +213,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_inside_function { fn with_super_inside_function() { use super::*; @@ -219,6 +221,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_further_inside { fn insidefoo() {} mod inner { diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 20e06d4b36641..1a5586cbb88d9 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -205,6 +205,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass { use super::*; @@ -213,6 +214,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_inside_function { fn with_super_inside_function() { use super::*; @@ -220,6 +222,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_further_inside { fn insidefoo() {} mod inner { diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 0c69d5262c26f..8e88f216394d2 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -106,31 +106,31 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports.rs:236:17 + --> tests/ui/wildcard_imports.rs:239:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> tests/ui/wildcard_imports.rs:244:13 + --> tests/ui/wildcard_imports.rs:247:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports.rs:253:17 + --> tests/ui/wildcard_imports.rs:256:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports.rs:262:13 + --> tests/ui/wildcard_imports.rs:265:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports.rs:270:13 + --> tests/ui/wildcard_imports.rs:273:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed index 6a9fe007d654d..197dd3b94df09 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -198,6 +198,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass { use super::*; @@ -206,6 +207,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_inside_function { fn with_super_inside_function() { use super::*; @@ -213,6 +215,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_further_inside { fn insidefoo() {} mod inner { diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr index 11e0bd377692d..66adacd95dcc1 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -106,31 +106,31 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:230:17 + --> tests/ui/wildcard_imports_2021.rs:233:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:238:13 + --> tests/ui/wildcard_imports_2021.rs:241:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:247:17 + --> tests/ui/wildcard_imports_2021.rs:250:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:256:13 + --> tests/ui/wildcard_imports_2021.rs:259:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:264:13 + --> tests/ui/wildcard_imports_2021.rs:267:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed index 6a9fe007d654d..197dd3b94df09 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -198,6 +198,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass { use super::*; @@ -206,6 +207,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_inside_function { fn with_super_inside_function() { use super::*; @@ -213,6 +215,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_further_inside { fn insidefoo() {} mod inner { diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr index 11e0bd377692d..66adacd95dcc1 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -106,31 +106,31 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:230:17 + --> tests/ui/wildcard_imports_2021.rs:233:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:238:13 + --> tests/ui/wildcard_imports_2021.rs:241:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:247:17 + --> tests/ui/wildcard_imports_2021.rs:250:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:256:13 + --> tests/ui/wildcard_imports_2021.rs:259:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> tests/ui/wildcard_imports_2021.rs:264:13 + --> tests/ui/wildcard_imports_2021.rs:267:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs index 18ebc0f512741..606ff080e774b 100644 --- a/tests/ui/wildcard_imports_2021.rs +++ b/tests/ui/wildcard_imports_2021.rs @@ -199,6 +199,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass { use super::*; @@ -207,6 +208,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_inside_function { fn with_super_inside_function() { use super::*; @@ -214,6 +216,7 @@ mod super_imports { } } + #[cfg(test)] mod test_should_pass_further_inside { fn insidefoo() {} mod inner { From d5e1acae82b68e6437e8ed4972328f8e6d6dc7ea Mon Sep 17 00:00:00 2001 From: Spencer Will Date: Thu, 4 Apr 2024 17:47:05 -0400 Subject: [PATCH 074/806] Add Expand All and Collapse All --- util/gh-pages/index.html | 18 +++++++++++------- util/gh-pages/script.js | 6 ++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 8de36fc40051c..d4cec81bcd987 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -57,20 +57,17 @@ background-color: var(--theme-hover); } - div.panel div.panel-body button.dropdown-toggle { + div.panel div.panel-body button { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); } - div.panel div.panel-body button.dropdown-toggle:hover { + div.panel div.panel-body button:hover { box-shadow: 0 0 3px var(--searchbar-shadow-color); } - div.panel div.panel-body .open button.dropdown-toggle { - background: var(--searchbar-bg); - color: var(--searchbar-fg); - border-color: var(--theme-popup-border); + div.panel div.panel-body button.open { filter: brightness(90%); } @@ -96,7 +93,6 @@ @media (min-width: 992px) { .search-control { margin-top: 0; - float: right; } } @@ -537,6 +533,14 @@

Clippy Lints

+
+ + +
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 921bb0376f6c3..661f80a6d3468 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -469,6 +469,12 @@ $location.path(lint.id); }; + $scope.toggleExpansion = function(lints, isExpanded) { + lints.forEach(lint => { + $scope.open[lint.id] = isExpanded; + }); + } + $scope.copyToClipboard = function (lint) { const clipboard = document.getElementById("clipboard-" + lint.id); if (clipboard) { From 8bfd992953937be4ba71833a5e64687b900701fb Mon Sep 17 00:00:00 2001 From: Spencer Will Date: Tue, 9 Apr 2024 15:32:46 -0400 Subject: [PATCH 075/806] Fix button styling on desktop and mobile views. --- util/gh-pages/index.html | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index d4cec81bcd987..9c82dafb2309e 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -357,6 +357,24 @@ opacity: 30%; } + .expansion-group { + margin-top: 15px; + padding: 0px 8px; + display: flex; + flex-wrap: nowrap; + } + + @media (min-width: 992px) { + .expansion-group { + margin-top: 0; + padding: 0px 15px; + } + } + + .expansion-control { + width: 50%; + } + :not(pre) > code { color: var(--inline-code-color); background-color: var(--inline-code-bg); @@ -533,7 +551,7 @@

Clippy Lints

-
+
From 6ae5fd958fdcef7232c7b349370a6fa58600ef4c Mon Sep 17 00:00:00 2001 From: Spencer Will Date: Sun, 7 Jul 2024 18:53:53 -0400 Subject: [PATCH 076/806] Remove event propagation calls. Adjust column spacing for filter panel to give more space for buttons on smaller screens. --- util/gh-pages/index.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 9c82dafb2309e..0c0f28e4fbd3d 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -419,7 +419,7 @@

Clippy Lints

-
+
-
+
Clippy Lints
- -
From 79d1ac66a8317083bb801a5b09ed180dacc8f70c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Jul 2024 21:59:28 -0400 Subject: [PATCH 077/806] Remove rustdoc tests which no longer parse --- src/tools/rustfmt/tests/source/type.rs | 2 -- src/tools/rustfmt/tests/target/negative-bounds.rs | 6 ------ src/tools/rustfmt/tests/target/type.rs | 2 -- 3 files changed, 10 deletions(-) diff --git a/src/tools/rustfmt/tests/source/type.rs b/src/tools/rustfmt/tests/source/type.rs index 61ef73a3cab1c..7a232f85198a8 100644 --- a/src/tools/rustfmt/tests/source/type.rs +++ b/src/tools/rustfmt/tests/source/type.rs @@ -146,8 +146,6 @@ trait T: ~ const Super {} const fn not_quite_const() -> i32 { ::CONST } -struct S(std::marker::PhantomData); - impl ~ const T {} fn apit(_: impl ~ const T) {} diff --git a/src/tools/rustfmt/tests/target/negative-bounds.rs b/src/tools/rustfmt/tests/target/negative-bounds.rs index 4fb35cccf6684..9fcb86ef4a429 100644 --- a/src/tools/rustfmt/tests/target/negative-bounds.rs +++ b/src/tools/rustfmt/tests/target/negative-bounds.rs @@ -3,9 +3,3 @@ where i32: !Copy, { } - -fn maybe_const_negative() -where - i32: ~const !Copy, -{ -} diff --git a/src/tools/rustfmt/tests/target/type.rs b/src/tools/rustfmt/tests/target/type.rs index c789ecb055a7d..325adb52f3f99 100644 --- a/src/tools/rustfmt/tests/target/type.rs +++ b/src/tools/rustfmt/tests/target/type.rs @@ -153,8 +153,6 @@ const fn not_quite_const() -> i32 { ::CONST } -struct S(std::marker::PhantomData); - impl ~const T {} fn apit(_: impl ~const T) {} From 78f6386b623c64160fa3475605c7ebfb065f62bf Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Fri, 12 Jul 2024 10:51:32 +0200 Subject: [PATCH 078/806] Clarify comment in tests/fail/tree_borrows/reservedim_spurious_write.rs Co-authored-by: Ralf Jung --- .../tests/fail/tree_borrows/reservedim_spurious_write.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs index 6ae79be6cc7e8..73f227fee2fcb 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs @@ -41,8 +41,9 @@ macro_rules! synchronized { } fn main() { - // The conflict occurs one one single location but the example involves - // lazily initialized permissions. + // The conflict occurs on one single location but the example involves + // lazily initialized permissions. We will use `&mut Cell<()>` references + // to `data` to achieve this. let mut data = 0u8; let ptr = SendPtr(std::ptr::addr_of_mut!(data)); let barrier = Arc::new(Barrier::new(2)); From edeb0fcddc80f600f7854abf2b421f3f8c799240 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Fri, 12 Jul 2024 11:12:01 +0200 Subject: [PATCH 079/806] Fix syntax errors in example code --- clippy_lints/src/casts/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 54f0c7c468719..e9f9084af073c 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -658,11 +658,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// let _: (0.0_f32 / 0.0) as u64; + /// let _ = (0.0_f32 / 0.0) as u64; /// ``` /// Use instead: /// ```rust,ignore - /// let _: = 0_u64; + /// let _ = 0_u64; /// ``` #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, From 983b4c37203e841c1a362e1cf3a16ef388269499 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 12 Jul 2024 17:23:08 +0800 Subject: [PATCH 080/806] [`unwrap_or_default`]: skip warning when calling inside of suggested method's implementation --- clippy_lints/src/methods/or_fun_call.rs | 23 ++++++++++++++++++----- tests/ui/or_fun_call.fixed | 12 ++++++++++++ tests/ui/or_fun_call.rs | 12 ++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 583e04fb4b18a..baa60454419b1 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -70,18 +70,31 @@ pub(super) fn check<'tcx>( }; let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| { + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) .into_iter() .flatten() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .any(|assoc| { - assoc.fn_has_self_parameter + .find_map(|assoc| { + if assoc.fn_has_self_parameter && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + { + Some(assoc.def_id) + } else { + None + } }) - }); - if !has_suggested_method { + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir().get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { return false; } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index c76f7a81843b2..3501559297188 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -318,4 +318,16 @@ fn host_effect() { Add::::add(1, 1).add(i32::MIN); } +mod issue_10228 { + struct Entry; + + impl Entry { + fn or_insert(self, _default: i32) {} + fn or_default(self) { + // Don't lint, suggested code is an infinite recursion + self.or_insert(Default::default()) + } + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 97cf496d3ac7c..3dcf657d74350 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -318,4 +318,16 @@ fn host_effect() { Add::::add(1, 1).add(i32::MIN); } +mod issue_10228 { + struct Entry; + + impl Entry { + fn or_insert(self, _default: i32) {} + fn or_default(self) { + // Don't lint, suggested code is an infinite recursion + self.or_insert(Default::default()) + } + } +} + fn main() {} From 1bafedbfe97e7b9d527a1eb6434f4b1fb4db2dd5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 12 Jul 2024 12:26:23 +0200 Subject: [PATCH 081/806] Implement rough symbol interning infra --- src/tools/rust-analyzer/.typos.toml | 2 + src/tools/rust-analyzer/Cargo.lock | 7 + .../rust-analyzer/crates/intern/Cargo.toml | 3 +- .../rust-analyzer/crates/intern/src/lib.rs | 3 + .../rust-analyzer/crates/intern/src/symbol.rs | 293 ++++++++++++++++++ .../crates/intern/src/symbol/symbols.rs | 236 ++++++++++++++ 6 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/intern/src/symbol.rs create mode 100644 src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index c2e8b265218fb..e7e764ce03517 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -14,6 +14,8 @@ extend-ignore-re = [ "\\w*\\.{3,4}\\w*", '"flate2"', "raison d'être", + "inout", + "optin" ] [default.extend-words] diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e9ebe26f42c2c..b165697724e05 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -835,6 +835,7 @@ dependencies = [ "dashmap", "hashbrown", "rustc-hash", + "sptr", "triomphe", ] @@ -1885,6 +1886,12 @@ dependencies = [ "vfs", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 67b4164ce1fe0..c08ecb5c307b1 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -18,6 +18,7 @@ dashmap.workspace = true hashbrown.workspace = true rustc-hash.workspace = true triomphe.workspace = true +sptr = "0.3.2" [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs index 40d18b1cf8677..868d03caff557 100644 --- a/src/tools/rust-analyzer/crates/intern/src/lib.rs +++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs @@ -20,6 +20,9 @@ type Guard = dashmap::RwLockWriteGuard< HashMap, SharedValue<()>, BuildHasherDefault>, >; +mod symbol; +pub use self::symbol::{symbols, Symbol}; + pub struct Interned { arc: Arc, } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs new file mode 100644 index 0000000000000..a1cffd0662f8e --- /dev/null +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -0,0 +1,293 @@ +//! Attempt at flexible symbol interning, allowing to intern and free strings at runtime while also +//! supporting + +use std::{ + borrow::Borrow, + fmt, + hash::{BuildHasherDefault, Hash, Hasher}, + mem, + ptr::NonNull, + sync::OnceLock, +}; + +use dashmap::{DashMap, SharedValue}; +use hashbrown::{hash_map::RawEntryMut, HashMap}; +use rustc_hash::FxHasher; +use sptr::Strict; +use triomphe::Arc; + +pub mod symbols; + +// some asserts for layout compatibility +const _: () = assert!(std::mem::size_of::>() == std::mem::size_of::<&str>()); +const _: () = assert!(std::mem::align_of::>() == std::mem::align_of::<&str>()); + +const _: () = assert!(std::mem::size_of::>>() == std::mem::size_of::<&&str>()); +const _: () = assert!(std::mem::align_of::>>() == std::mem::align_of::<&&str>()); + +/// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or +/// `Arc>` but its size is that of a thin pointer. The active variant is encoded as a tag +/// in the LSB of the alignment niche. +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +struct TaggedArcPtr { + packed: NonNull<*const str>, +} + +unsafe impl Send for TaggedArcPtr {} +unsafe impl Sync for TaggedArcPtr {} + +impl TaggedArcPtr { + const BOOL_BITS: usize = true as usize; + + const fn non_arc(r: &&str) -> Self { + Self { + // SAFETY: The pointer is non-null as it is derived from a reference + // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the + // packing stuff requires reading out the pointer to an integer which is not supported + // in const contexts, so here we make use of the fact that for the non-arc version the + // tag is false (0) and thus does not need touching the actual pointer value.ext) + packed: unsafe { + NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut()) + }, + } + } + + fn arc(arc: Arc>) -> Self { + Self { + packed: Self::pack_arc( + // Safety: `Arc::into_raw` always returns a non null pointer + unsafe { NonNull::new_unchecked(Arc::into_raw(arc).cast_mut().cast()) }, + ), + } + } + + /// Retrieves the tag. + #[inline] + pub(crate) fn try_as_arc_owned(self) -> Option>> { + // Unpack the tag from the alignment niche + let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS; + if tag != 0 { + // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` + Some(unsafe { Arc::from_raw(self.pointer().as_ptr().cast::>()) }) + } else { + None + } + } + + #[inline] + const fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { + let packed_tag = true as usize; + + // can't use this strict provenance stuff here due to trait methods not being const + // unsafe { + // // Safety: The pointer is derived from a non-null + // NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| { + // // Safety: + // // - The pointer is `NonNull` => it's address is `NonZero` + // // - `P::BITS` least significant bits are always zero (`Pointer` contract) + // // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) + // // + // // Thus `addr >> T::BITS` is guaranteed to be non-zero. + // // + // // `{non_zero} | packed_tag` can't make the value zero. + + // (addr >> Self::BOOL_BITS) | packed_tag + // })) + // } + // so what follows is roughly what the above looks like but inlined + + let self_addr = unsafe { core::mem::transmute::<*const _, usize>(ptr.as_ptr()) }; + let addr = self_addr | packed_tag; + let dest_addr = addr as isize; + let offset = dest_addr.wrapping_sub(self_addr as isize); + + // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes + unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::().wrapping_offset(offset).cast()) } + } + + #[inline] + pub(crate) fn pointer(self) -> NonNull<*const str> { + // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes + unsafe { + NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| { + addr & !Self::BOOL_BITS + })) + } + } + + #[inline] + pub(crate) fn as_str(&self) -> &str { + // SAFETY: We always point to a pointer to a str no matter what variant is active + unsafe { *self.pointer().as_ptr().cast::<&str>() } + } +} + +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct Symbol { + repr: TaggedArcPtr, +} +const _: () = assert!(std::mem::size_of::() == std::mem::size_of::>()); +const _: () = assert!(std::mem::align_of::() == std::mem::align_of::>()); + +static MAP: OnceLock>> = OnceLock::new(); + +impl Symbol { + pub fn intern(s: &str) -> Self { + let (mut shard, hash) = Self::select_shard(s); + // Atomically, + // - check if `obj` is already in the map + // - if so, copy out its entry, conditionally bumping the backing Arc and return it + // - if not, put it into a box and then into an Arc, insert it, bump the ref-count and return the copy + // This needs to be atomic (locking the shard) to avoid races with other thread, which could + // insert the same object between us looking it up and inserting it. + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, s) { + RawEntryMut::Occupied(occ) => Self { repr: increase_arc_refcount(occ.key().0) }, + RawEntryMut::Vacant(vac) => Self { + repr: increase_arc_refcount( + vac.insert_hashed_nocheck( + hash, + SymbolProxy(TaggedArcPtr::arc(Arc::new(Box::::from(s)))), + SharedValue::new(()), + ) + .0 + .0, + ), + }, + } + } + + pub fn as_str(&self) -> &str { + self.repr.as_str() + } + + #[inline] + fn select_shard( + s: &str, + ) -> ( + dashmap::RwLockWriteGuard< + 'static, + HashMap, BuildHasherDefault>, + >, + u64, + ) { + let storage = MAP.get_or_init(symbols::prefill); + let hash = { + let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher()); + s.hash(&mut hasher); + hasher.finish() + }; + let shard_idx = storage.determine_shard(hash as usize); + let shard = &storage.shards()[shard_idx]; + (shard.write(), hash) + } + + #[cold] + fn drop_slow(arc: &Arc>) { + let (mut shard, hash) = Self::select_shard(arc); + + if Arc::count(arc) != 2 { + // Another thread has interned another copy + return; + } + + match shard.raw_entry_mut().from_key_hashed_nocheck::(hash, arc.as_ref()) { + RawEntryMut::Occupied(occ) => occ.remove_entry(), + RawEntryMut::Vacant(_) => unreachable!(), + } + .0 + .0 + .try_as_arc_owned() + .unwrap(); + + // Shrink the backing storage if the shard is less than 50% occupied. + if shard.len() * 2 < shard.capacity() { + shard.shrink_to_fit(); + } + } +} + +impl Drop for Symbol { + #[inline] + fn drop(&mut self) { + let Some(arc) = self.repr.try_as_arc_owned() else { + return; + }; + // When the last `Ref` is dropped, remove the object from the global map. + if Arc::count(&arc) == 2 { + // Only `self` and the global map point to the object. + + Self::drop_slow(&arc); + } + // decrement the ref count + drop(arc); + } +} + +fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr { + let Some(arc) = repr.try_as_arc_owned() else { + return repr; + }; + // increase the ref count + mem::forget(arc.clone()); + mem::forget(arc); + repr +} + +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} + +// only exists so we can use `from_key_hashed_nocheck` with a &str +#[derive(Debug, PartialEq, Eq)] +struct SymbolProxy(TaggedArcPtr); + +impl Hash for SymbolProxy { + fn hash(&self, state: &mut H) { + self.0.as_str().hash(state); + } +} + +impl Borrow for SymbolProxy { + fn borrow(&self) -> &str { + self.0.as_str() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn smoke_test() { + Symbol::intern("isize"); + let base_len = MAP.get().unwrap().len(); + let hello = Symbol::intern("hello"); + let world = Symbol::intern("world"); + let bang = Symbol::intern("!"); + let q = Symbol::intern("?"); + assert_eq!(MAP.get().unwrap().len(), base_len + 4); + let bang2 = Symbol::intern("!"); + assert_eq!(MAP.get().unwrap().len(), base_len + 4); + drop(bang2); + assert_eq!(MAP.get().unwrap().len(), base_len + 4); + drop(q); + assert_eq!(MAP.get().unwrap().len(), base_len + 3); + let default = Symbol::intern("default"); + assert_eq!(MAP.get().unwrap().len(), base_len + 3); + assert_eq!( + "hello default world!", + format!("{} {} {}{}", hello.as_str(), default.as_str(), world.as_str(), bang.as_str()) + ); + drop(default); + assert_eq!( + "hello world!", + format!("{} {}{}", hello.as_str(), world.as_str(), bang.as_str()) + ); + drop(hello); + drop(world); + drop(bang); + assert_eq!(MAP.get().unwrap().len(), base_len); + } +} diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs new file mode 100644 index 0000000000000..d2ca4401b66e9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -0,0 +1,236 @@ +#![allow(non_upper_case_globals)] + +use std::hash::{BuildHasherDefault, Hash as _, Hasher as _}; + +use dashmap::{DashMap, SharedValue}; +use rustc_hash::FxHasher; + +use crate::{ + symbol::{SymbolProxy, TaggedArcPtr}, + Symbol, +}; +macro_rules! define_symbols { + ($($name:ident),* $(,)?) => { + $( + pub const $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; + )* + + + pub(super) fn prefill() -> DashMap> { + let mut dashmap_ = >>::with_hasher(BuildHasherDefault::default()); + + let hash_thing_ = |hasher_: &BuildHasherDefault, it_: &SymbolProxy| { + let mut hasher_ = std::hash::BuildHasher::build_hasher(hasher_); + it_.hash(&mut hasher_); + hasher_.finish() + }; + { + $( + + let proxy_ = SymbolProxy($name.repr); + let hash_ = hash_thing_(dashmap_.hasher(), &proxy_); + let shard_idx_ = dashmap_.determine_shard(hash_ as usize); + dashmap_.shards_mut()[shard_idx_].get_mut().raw_entry_mut().from_hash(hash_, |k| k == &proxy_).insert(proxy_, SharedValue::new(())); + )* + } + dashmap_ + } + }; +} +define_symbols! { + add_assign, + add, + alloc, + as_str, + asm, + assert, + bench, + bitand_assign, + bitand, + bitor_assign, + bitor, + bitxor_assign, + bitxor, + bool, + Box, + boxed, + branch, + call_mut, + call_once, + call, + Center, + cfg_accessible, + cfg_attr, + cfg_eval, + cfg, + char, + Clone, + column, + compile_error, + concat_bytes, + concat_idents, + concat, + const_format_args, + Copy, + core_panic, + core, + crate_type, + Debug, + default, + Default, + deref_mut, + deref, + derive_const, + derive, + div_assign, + div, + doc, + drop, + env, + eq, + Eq, + f128, + f16, + f32, + f64, + feature, + file, + filter_map, + fmt, + fn_mut, + fn_once, + format_args_nl, + format_args, + format, + from_usize, + future_trait, + future, + Future, + ge, + global_allocator, + global_asm, + gt, + Hash, + i128, + i16, + i32, + i64, + i8, + Implied, + include_bytes, + include_str, + include, + index_mut, + index, + Index, + into_future, + IntoFuture, + IntoIter, + IntoIterator, + is_empty, + Is, + isize, + Item, + iter_mut, + iter, + Iterator, + le, + Left, + len, + line, + llvm_asm, + log_syntax, + lt, + macro_rules, + module_path, + mul_assign, + mul, + ne, + neg, + Neg, + new_binary, + new_debug, + new_display, + new_lower_exp, + new_lower_hex, + new_octal, + new_pointer, + new_upper_exp, + new_upper_hex, + new_v1_formatted, + new, + next, + no_core, + no_std, + none, + None, + not, + Not, + Ok, + ops, + option_env, + option, + Option, + Ord, + Output, + owned_box, + panic_2015, + panic_2021, + Param, + partial_ord, + PartialEq, + PartialOrd, + pieces, + poll, + prelude, + quote, + r#fn, + Range, + RangeFrom, + RangeFull, + RangeInclusive, + RangeTo, + RangeToInclusive, + recursion_limit, + register_attr, + register_tool, + rem_assign, + rem, + result, + Result, + Right, + rust_2015, + rust_2018, + rust_2021, + rust_2024, + shl_assign, + shl, + shr_assign, + shr, + std_panic, + std, + str, + string, + String, + stringify, + sub_assign, + sub, + Target, + test_case, + test, + trace_macros, + Try, + u128, + u16, + u32, + u64, + u8, + Unknown, + unreachable_2015, + unreachable_2021, + unreachable, + unsafe_cell, + usize, + v1, + va_list +} From e805055b0ea7595fd7400b7583fa0b6ff11264bf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 12 Jul 2024 14:11:28 +0200 Subject: [PATCH 082/806] Add missing docs --- src/tools/rust-analyzer/crates/intern/src/symbol.rs | 2 +- src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index a1cffd0662f8e..290657a3d3627 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -39,7 +39,7 @@ unsafe impl Sync for TaggedArcPtr {} impl TaggedArcPtr { const BOOL_BITS: usize = true as usize; - const fn non_arc(r: &&str) -> Self { + const fn non_arc(r: &'static &'static str) -> Self { Self { // SAFETY: The pointer is non-null as it is derived from a reference // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index d2ca4401b66e9..a1cf6e0941466 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -1,3 +1,4 @@ +//! Module defining all known symbols required by the rest of rust-analyzer. #![allow(non_upper_case_globals)] use std::hash::{BuildHasherDefault, Hash as _, Hasher as _}; From 602da3fadd919c41378b4b1b694ae1c391ace6d8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 12 Jul 2024 15:57:54 +0200 Subject: [PATCH 083/806] Use Symbol in Name --- .../rust-analyzer/crates/hir-def/src/attr.rs | 9 +- .../crates/hir-def/src/body/lower.rs | 114 ++--- .../crates/hir-def/src/builtin_type.rs | 85 ++-- .../rust-analyzer/crates/hir-def/src/data.rs | 4 +- .../rust-analyzer/crates/hir-def/src/db.rs | 8 +- .../crates/hir-def/src/expander.rs | 6 + .../crates/hir-def/src/find_path.rs | 25 +- .../crates/hir-def/src/hir/format_args.rs | 6 +- .../crates/hir-def/src/item_tree/lower.rs | 11 +- .../crates/hir-def/src/lang_item.rs | 13 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-def/src/nameres/collector.rs | 90 ++-- .../crates/hir-def/src/path/lower.rs | 10 +- .../crates/hir-def/src/resolver.rs | 25 +- .../crates/hir-expand/src/attrs.rs | 91 ++-- .../hir-expand/src/builtin_attr_macro.rs | 3 +- .../hir-expand/src/builtin_derive_macro.rs | 3 +- .../crates/hir-expand/src/builtin_fn_macro.rs | 15 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 9 + .../crates/hir-expand/src/declarative.rs | 7 +- .../crates/hir-expand/src/mod_path.rs | 30 +- .../crates/hir-expand/src/name.rs | 391 ++++-------------- .../crates/hir-expand/src/quote.rs | 2 + .../crates/hir-ty/src/autoderef.rs | 6 +- .../crates/hir-ty/src/chalk_db.rs | 9 +- .../hir-ty/src/diagnostics/decl_check.rs | 2 +- .../crates/hir-ty/src/diagnostics/expr.rs | 4 +- .../crates/hir-ty/src/display.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 +- .../crates/hir-ty/src/infer/closure.rs | 13 +- .../crates/hir-ty/src/infer/expr.rs | 15 +- .../crates/hir-ty/src/infer/mutability.rs | 15 +- .../crates/hir-ty/src/infer/path.rs | 3 +- .../crates/hir-ty/src/infer/unify.rs | 6 +- .../crates/hir-ty/src/lang_items.rs | 54 +-- .../rust-analyzer/crates/hir-ty/src/lib.rs | 7 +- .../crates/hir-ty/src/mir/eval.rs | 37 +- .../crates/hir-ty/src/mir/eval/shim.rs | 23 +- .../crates/hir-ty/src/mir/lower.rs | 6 +- .../crates/hir-ty/src/mir/lower/as_place.rs | 13 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 9 +- .../rust-analyzer/crates/hir/src/attrs.rs | 3 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 15 +- .../crates/hir/src/source_analyzer.rs | 35 +- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- .../ide-assists/src/handlers/bool_to_enum.rs | 2 +- .../src/handlers/convert_bool_then.rs | 4 +- .../handlers/convert_iter_for_each_to_for.rs | 8 +- .../handlers/destructure_struct_binding.rs | 4 +- .../handlers/generate_is_empty_from_len.rs | 6 +- .../ide-assists/src/handlers/inline_call.rs | 7 +- .../src/handlers/replace_method_eager_lazy.rs | 4 +- .../crates/ide-assists/src/utils.rs | 4 +- .../crates/ide-completion/src/completions.rs | 4 +- .../src/completions/attribute/macro_use.rs | 2 +- .../ide-completion/src/completions/dot.rs | 9 +- .../ide-completion/src/completions/expr.rs | 11 +- .../src/completions/lifetime.rs | 4 +- .../ide-completion/src/completions/use_.rs | 5 +- .../ide-completion/src/context/analysis.rs | 6 +- .../crates/ide-completion/src/render.rs | 6 +- .../ide-completion/src/render/function.rs | 10 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 6 +- .../crates/ide-db/src/path_transform.rs | 2 +- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/unresolved_field.rs | 2 +- .../crates/ide/src/goto_definition.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 4 +- .../ide/src/inlay_hints/generic_param.rs | 2 +- .../crates/ide/src/test_explorer.rs | 11 +- .../crates/ide/src/view_memory_layout.rs | 15 +- .../rust-analyzer/crates/intern/src/lib.rs | 2 +- .../rust-analyzer/crates/intern/src/symbol.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 121 +++++- 75 files changed, 756 insertions(+), 761 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 184dab8367c16..a1ffb8c0a7596 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -9,6 +9,7 @@ use hir_expand::{ attrs::{collect_attrs, Attr, AttrId, RawAttrs}, HirFileId, InFile, }; +use intern::sym; use la_arena::{ArenaMap, Idx, RawIdx}; use mbe::DelimiterKind; use syntax::{ @@ -199,8 +200,8 @@ impl Attrs { .segments() .iter() .rev() - .zip(["core", "prelude", "v1", "test"].iter().rev()) - .all(|it| it.0.as_str() == Some(it.1)) + .zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev()) + .all(|it| it.0 == it.1) }) } @@ -568,6 +569,10 @@ impl<'attr> AttrQuery<'attr> { self.attrs().find_map(|attr| attr.string_value()) } + pub fn string_value_with_span(self) -> Option<(&'attr str, span::Span)> { + self.attrs().find_map(|attr| attr.string_value_with_span()) + } + pub fn string_value_unescape(self) -> Option> { self.attrs().find_map(|attr| attr.string_value_unescape()) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index be7068c807a5f..83ba8f41931e2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -5,10 +5,10 @@ use std::mem; use base_db::CrateId; use hir_expand::{ - name::{name, AsName, Name}, + name::{AsName, Name}, ExpandError, InFile, }; -use intern::Interned; +use intern::{sym, Interned}; use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::AstIdMap; @@ -187,8 +187,10 @@ impl ExprCollector<'_> { { let is_mutable = self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let binding_id: la_arena::Idx = - self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false)); + let binding_id: la_arena::Idx = self.alloc_binding( + Name::new_symbol_root(sym::self_), + BindingAnnotation::new(is_mutable, false), + ); self.body.self_param = Some(binding_id); self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } @@ -1588,18 +1590,22 @@ impl ExprCollector<'_> { }); let mut mappings = vec![]; let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { - Some((s, is_direct_literal)) => format_args::parse( - &s, - fmt_snippet, - args, - is_direct_literal, - |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), - |name, span| { - if let Some(span) = span { - mappings.push((span, name)) - } - }, - ), + Some((s, is_direct_literal)) => { + let call_ctx = self.expander.syntax_context(); + format_args::parse( + &s, + fmt_snippet, + args, + is_direct_literal, + |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), + |name, span| { + if let Some(span) = span { + mappings.push((span, name)) + } + }, + call_ctx, + ) + } None => FormatArgs { template: Default::default(), arguments: args.finish(), @@ -1723,14 +1729,18 @@ impl ExprCollector<'_> { // unsafe { ::core::fmt::UnsafeArg::new() } // ) - let Some(new_v1_formatted) = - LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted]) - else { + let Some(new_v1_formatted) = LangItem::FormatArguments.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new_v1_formatted), + ) else { return self.missing_expr(); }; - let Some(unsafe_arg_new) = - LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new]) - else { + let Some(unsafe_arg_new) = LangItem::FormatUnsafeArg.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new), + ) else { return self.missing_expr(); }; let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted)); @@ -1812,10 +1822,10 @@ impl ExprCollector<'_> { self.db, self.krate, match alignment { - Some(FormatAlignment::Left) => name![Left], - Some(FormatAlignment::Right) => name![Right], - Some(FormatAlignment::Center) => name![Center], - None => name![Unknown], + Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left), + Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right), + Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center), + None => Name::new_symbol_root(sym::Unknown), }, ); match align { @@ -1838,8 +1848,11 @@ impl ExprCollector<'_> { let width = self.make_count(width, argmap); let format_placeholder_new = { - let format_placeholder_new = - LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]); + let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new), + ); match format_placeholder_new { Some(path) => self.alloc_expr_desugared(Expr::Path(path)), None => self.missing_expr(), @@ -1883,11 +1896,14 @@ impl ExprCollector<'_> { *n as u128, Some(BuiltinUint::Usize), ))); - let count_is = - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { - Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), - None => self.missing_expr(), - }; + let count_is = match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::Is), + ) { + Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), + None => self.missing_expr(), + }; self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]), @@ -1905,7 +1921,7 @@ impl ExprCollector<'_> { let count_param = match LangItem::FormatCount.ty_rel_path( self.db, self.krate, - name![Param], + Name::new_symbol_root(sym::Param), ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), @@ -1921,7 +1937,11 @@ impl ExprCollector<'_> { self.missing_expr() } } - None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) { + None => match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::Implied), + ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), }, @@ -1942,18 +1962,18 @@ impl ExprCollector<'_> { let new_fn = match LangItem::FormatArgument.ty_rel_path( self.db, self.krate, - match ty { - Format(Display) => name![new_display], - Format(Debug) => name![new_debug], - Format(LowerExp) => name![new_lower_exp], - Format(UpperExp) => name![new_upper_exp], - Format(Octal) => name![new_octal], - Format(Pointer) => name![new_pointer], - Format(Binary) => name![new_binary], - Format(LowerHex) => name![new_lower_hex], - Format(UpperHex) => name![new_upper_hex], - Usize => name![from_usize], - }, + Name::new_symbol_root(match ty { + Format(Display) => sym::new_display, + Format(Debug) => sym::new_debug, + Format(LowerExp) => sym::new_lower_exp, + Format(UpperExp) => sym::new_upper_exp, + Format(Octal) => sym::new_octal, + Format(Pointer) => sym::new_pointer, + Format(Binary) => sym::new_binary, + Format(LowerHex) => sym::new_lower_hex, + Format(UpperHex) => sym::new_upper_hex, + Usize => sym::from_usize, + }), ) { Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs index f9e55559dab43..2243f514fe102 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs @@ -5,7 +5,8 @@ use std::fmt; -use hir_expand::name::{name, AsName, Name}; +use hir_expand::name::{AsName, Name}; +use intern::sym; /// Different signed int types. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BuiltinInt { @@ -49,28 +50,28 @@ pub enum BuiltinType { impl BuiltinType { #[rustfmt::skip] pub const ALL: &'static [(Name, BuiltinType)] = &[ - (name![char], BuiltinType::Char), - (name![bool], BuiltinType::Bool), - (name![str], BuiltinType::Str), - - (name![isize], BuiltinType::Int(BuiltinInt::Isize)), - (name![i8], BuiltinType::Int(BuiltinInt::I8)), - (name![i16], BuiltinType::Int(BuiltinInt::I16)), - (name![i32], BuiltinType::Int(BuiltinInt::I32)), - (name![i64], BuiltinType::Int(BuiltinInt::I64)), - (name![i128], BuiltinType::Int(BuiltinInt::I128)), - - (name![usize], BuiltinType::Uint(BuiltinUint::Usize)), - (name![u8], BuiltinType::Uint(BuiltinUint::U8)), - (name![u16], BuiltinType::Uint(BuiltinUint::U16)), - (name![u32], BuiltinType::Uint(BuiltinUint::U32)), - (name![u64], BuiltinType::Uint(BuiltinUint::U64)), - (name![u128], BuiltinType::Uint(BuiltinUint::U128)), - - (name![f16], BuiltinType::Float(BuiltinFloat::F16)), - (name![f32], BuiltinType::Float(BuiltinFloat::F32)), - (name![f64], BuiltinType::Float(BuiltinFloat::F64)), - (name![f128], BuiltinType::Float(BuiltinFloat::F128)), + (Name::new_symbol_root(sym::char), BuiltinType::Char), + (Name::new_symbol_root(sym::bool), BuiltinType::Bool), + (Name::new_symbol_root(sym::str), BuiltinType::Str), + + (Name::new_symbol_root(sym::isize), BuiltinType::Int(BuiltinInt::Isize)), + (Name::new_symbol_root(sym::i8), BuiltinType::Int(BuiltinInt::I8)), + (Name::new_symbol_root(sym::i16), BuiltinType::Int(BuiltinInt::I16)), + (Name::new_symbol_root(sym::i32), BuiltinType::Int(BuiltinInt::I32)), + (Name::new_symbol_root(sym::i64), BuiltinType::Int(BuiltinInt::I64)), + (Name::new_symbol_root(sym::i128), BuiltinType::Int(BuiltinInt::I128)), + + (Name::new_symbol_root(sym::usize), BuiltinType::Uint(BuiltinUint::Usize)), + (Name::new_symbol_root(sym::u8), BuiltinType::Uint(BuiltinUint::U8)), + (Name::new_symbol_root(sym::u16), BuiltinType::Uint(BuiltinUint::U16)), + (Name::new_symbol_root(sym::u32), BuiltinType::Uint(BuiltinUint::U32)), + (Name::new_symbol_root(sym::u64), BuiltinType::Uint(BuiltinUint::U64)), + (Name::new_symbol_root(sym::u128), BuiltinType::Uint(BuiltinUint::U128)), + + (Name::new_symbol_root(sym::f16), BuiltinType::Float(BuiltinFloat::F16)), + (Name::new_symbol_root(sym::f32), BuiltinType::Float(BuiltinFloat::F32)), + (Name::new_symbol_root(sym::f64), BuiltinType::Float(BuiltinFloat::F64)), + (Name::new_symbol_root(sym::f128), BuiltinType::Float(BuiltinFloat::F128)), ]; pub fn by_name(name: &Name) -> Option { @@ -81,30 +82,30 @@ impl BuiltinType { impl AsName for BuiltinType { fn as_name(&self) -> Name { match self { - BuiltinType::Char => name![char], - BuiltinType::Bool => name![bool], - BuiltinType::Str => name![str], + BuiltinType::Char => Name::new_symbol_root(sym::char), + BuiltinType::Bool => Name::new_symbol_root(sym::bool), + BuiltinType::Str => Name::new_symbol_root(sym::str), BuiltinType::Int(it) => match it { - BuiltinInt::Isize => name![isize], - BuiltinInt::I8 => name![i8], - BuiltinInt::I16 => name![i16], - BuiltinInt::I32 => name![i32], - BuiltinInt::I64 => name![i64], - BuiltinInt::I128 => name![i128], + BuiltinInt::Isize => Name::new_symbol_root(sym::isize), + BuiltinInt::I8 => Name::new_symbol_root(sym::i8), + BuiltinInt::I16 => Name::new_symbol_root(sym::i16), + BuiltinInt::I32 => Name::new_symbol_root(sym::i32), + BuiltinInt::I64 => Name::new_symbol_root(sym::i64), + BuiltinInt::I128 => Name::new_symbol_root(sym::i128), }, BuiltinType::Uint(it) => match it { - BuiltinUint::Usize => name![usize], - BuiltinUint::U8 => name![u8], - BuiltinUint::U16 => name![u16], - BuiltinUint::U32 => name![u32], - BuiltinUint::U64 => name![u64], - BuiltinUint::U128 => name![u128], + BuiltinUint::Usize => Name::new_symbol_root(sym::usize), + BuiltinUint::U8 => Name::new_symbol_root(sym::u8), + BuiltinUint::U16 => Name::new_symbol_root(sym::u16), + BuiltinUint::U32 => Name::new_symbol_root(sym::u32), + BuiltinUint::U64 => Name::new_symbol_root(sym::u64), + BuiltinUint::U128 => Name::new_symbol_root(sym::u128), }, BuiltinType::Float(it) => match it { - BuiltinFloat::F16 => name![f16], - BuiltinFloat::F32 => name![f32], - BuiltinFloat::F64 => name![f64], - BuiltinFloat::F128 => name![f128], + BuiltinFloat::F16 => Name::new_symbol_root(sym::f16), + BuiltinFloat::F32 => Name::new_symbol_root(sym::f32), + BuiltinFloat::F64 => Name::new_symbol_root(sym::f64), + BuiltinFloat::F128 => Name::new_symbol_root(sym::f128), }, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 55043fdc4b054..f60a433c4dafe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -6,7 +6,7 @@ use base_db::CrateId; use hir_expand::{ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind, }; -use intern::Interned; +use intern::{sym, Interned}; use smallvec::SmallVec; use syntax::{ast, Parse}; use triomphe::Arc; @@ -485,7 +485,7 @@ impl ExternCrateDeclData { let name = extern_crate.name.clone(); let krate = loc.container.krate(); - let crate_id = if name == hir_expand::name![self] { + let crate_id = if name == sym::self_ { Some(krate) } else { db.crate_def_map(krate) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 0eb9e7d30b251..0a14904f0bceb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -2,7 +2,7 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase, Upcast}; use either::Either; use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId}; -use intern::Interned; +use intern::{sym, Interned}; use la_arena::ArenaMap; use span::MacroCallId; use syntax::{ast, AstPtr}; @@ -261,9 +261,9 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { let item_tree = db.file_item_tree(file.into()); let attrs = item_tree.raw_attrs(AttrOwner::TopLevel); for attr in &**attrs { - match attr.path().as_ident().and_then(|id| id.as_text()) { - Some(ident) if ident == "no_std" => return true, - Some(ident) if ident == "cfg_attr" => {} + match attr.path().as_ident() { + Some(ident) if *ident == sym::no_std => return true, + Some(ident) if *ident == sym::cfg_attr => {} _ => continue, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs index dbf8e6b225c2a..d1640ad7e5b7b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -10,6 +10,7 @@ use hir_expand::{ InFile, MacroCallId, }; use limit::Limit; +use span::SyntaxContextId; use syntax::{ast, Parse}; use triomphe::Arc; @@ -52,6 +53,11 @@ impl Expander { self.module.krate } + pub fn syntax_context(&self) -> SyntaxContextId { + // FIXME: + SyntaxContextId::ROOT + } + pub fn enter_expand( &mut self, db: &dyn DefDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 9a3c0495414ca..e32add93c5526 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -3,9 +3,10 @@ use std::{cell::Cell, cmp::Ordering, iter}; use hir_expand::{ - name::{known, AsName, Name}, + name::{AsName, Name}, Lookup, }; +use intern::{sym, Symbol}; use rustc_hash::FxHashSet; use crate::{ @@ -414,13 +415,13 @@ fn select_best_path( (Unstable, Stable) => return new_path, _ => {} } - const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; + const STD_CRATES: [Symbol; 3] = [sym::std, sym::core, sym::alloc]; let choose = |new: (ModPath, _), old: (ModPath, _)| { let (new_path, _) = &new; let (old_path, _) = &old; - let new_has_prelude = new_path.segments().iter().any(|seg| seg == &known::prelude); - let old_has_prelude = old_path.segments().iter().any(|seg| seg == &known::prelude); + let new_has_prelude = new_path.segments().iter().any(|seg| *seg == sym::prelude); + let old_has_prelude = old_path.segments().iter().any(|seg| *seg == sym::prelude); match (new_has_prelude, old_has_prelude, cfg.prefer_prelude) { (true, false, true) | (false, true, false) => new, (true, false, false) | (false, true, true) => old, @@ -441,18 +442,20 @@ fn select_best_path( }; match (old_path.0.segments().first(), new_path.0.segments().first()) { - (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { + (Some(old), Some(new)) + if STD_CRATES.contains(old.symbol()) && STD_CRATES.contains(new.symbol()) => + { let rank = match cfg.prefer_no_std { false => |name: &Name| match name { - name if name == &known::core => 0, - name if name == &known::alloc => 1, - name if name == &known::std => 2, + name if *name == sym::core => 0, + name if *name == sym::alloc => 1, + name if *name == sym::std => 2, _ => unreachable!(), }, true => |name: &Name| match name { - name if name == &known::core => 2, - name if name == &known::alloc => 1, - name if name == &known::std => 0, + name if *name == sym::core => 2, + name if *name == sym::alloc => 1, + name if *name == sym::std => 0, _ => unreachable!(), }, }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index de0fa982c86a2..d6dd5df37366a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -3,10 +3,11 @@ use std::mem; use hir_expand::name::Name; use rustc_parse_format as parse; +use span::SyntaxContextId; use stdx::TupleExt; use syntax::{ ast::{self, IsString}, - SmolStr, TextRange, TextSize, + TextRange, TextSize, }; use crate::hir::ExprId; @@ -174,6 +175,7 @@ pub(crate) fn parse( is_direct_literal: bool, mut synth: impl FnMut(Name) -> ExprId, mut record_usage: impl FnMut(Name, Option), + call_ctx: SyntaxContextId, ) -> FormatArgs { let Ok(text) = s.value() else { return FormatArgs { @@ -248,7 +250,7 @@ pub(crate) fn parse( } } ArgRef::Name(name, span) => { - let name = Name::new_text_dont_use(SmolStr::new(name)); + let name = Name::new(name, call_ctx); if let Some((index, _)) = args.by_name(&name) { record_usage(name, span); // Name found in `args`, so we resolve it to its index. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 199b8daa37efe..496686b4f943c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -2,7 +2,8 @@ use std::collections::hash_map::Entry; -use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId}; +use hir_expand::{mod_path::path, name::AsName, span_map::SpanMapRef, HirFileId}; +use intern::sym; use la_arena::Arena; use rustc_hash::FxHashMap; use span::{AstIdMap, SyntaxContextId}; @@ -323,7 +324,7 @@ impl<'a> Ctx<'a> { let self_type = match self_param.ty() { Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), None => { - let self_type = TypeRef::Path(name![Self].into()); + let self_type = TypeRef::Path(Name::new_symbol_root(sym::Self_).into()); match self_param.kind() { ast::SelfParamKind::Owned => self_type, ast::SelfParamKind::Ref => TypeRef::Reference( @@ -669,7 +670,7 @@ impl<'a> Ctx<'a> { // Traits and trait aliases get the Self type as an implicit first type parameter. generics.type_or_consts.alloc( TypeParamData { - name: Some(name![Self]), + name: Some(Name::new_symbol_root(sym::Self_)), default: None, provenance: TypeParamProvenance::TraitSelf, } @@ -680,7 +681,7 @@ impl<'a> Ctx<'a> { generics.fill_bounds( &self.body_ctx, bounds, - Either::Left(TypeRef::Path(name![Self].into())), + Either::Left(TypeRef::Path(Name::new_symbol_root(sym::Self_).into())), ); } @@ -745,7 +746,7 @@ fn desugar_future_path(orig: TypeRef) -> Path { let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments().len() - 1).collect(); let binding = AssociatedTypeBinding { - name: name![Output], + name: Name::new_symbol_root(sym::Output), args: None, type_ref: Some(orig), bounds: Box::default(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 6d7836d5ae899..4d17650285f40 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -3,6 +3,7 @@ //! This attribute to tell the compiler about semi built-in std library //! features, such as Fn family of traits. use hir_expand::name::Name; +use intern::{sym, Symbol}; use rustc_hash::FxHashMap; use syntax::SmolStr; use triomphe::Arc; @@ -267,6 +268,14 @@ macro_rules! language_item_table { _ => None, } } + + /// Opposite of [`LangItem::name`] + pub fn from_symbol(sym: Symbol) -> Option { + match sym { + $(sym if sym == $module::$name => Some(LangItem::$variant), )* + _ => None, + } + } } } } @@ -274,7 +283,7 @@ macro_rules! language_item_table { impl LangItem { /// Opposite of [`LangItem::name`] pub fn from_name(name: &hir_expand::name::Name) -> Option { - Self::from_str(name.as_str()?) + Self::from_str(name.as_str()) } pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option { @@ -360,7 +369,7 @@ language_item_table! { DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; - Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index fc026a14d442f..a33e681cb5a76 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -862,7 +862,7 @@ impl GeneralConstId { .const_data(const_id) .name .as_ref() - .and_then(|it| it.as_str()) + .map(|it| it.as_str()) .unwrap_or("_") .to_owned(), GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index b5045efb62157..30d4a79a808d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -13,11 +13,11 @@ use hir_expand::{ builtin_attr_macro::find_builtin_attr, builtin_derive_macro::find_builtin_derive, builtin_fn_macro::find_builtin_macro, - name::{name, AsName, Name}, + name::{AsName, Name}, proc_macro::CustomProcMacroExpander, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; -use intern::Interned; +use intern::{sym, Interned}; use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; @@ -76,25 +76,28 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI let proc_macros = if krate.is_proc_macro { match db.proc_macros().get(&def_map.krate) { - Some(Ok(proc_macros)) => Ok(proc_macros - .iter() - .enumerate() - .map(|(idx, it)| { - let name = Name::new_text_dont_use(it.name.clone()); - ( - name, - if !db.expand_proc_attr_macros() { - CustomProcMacroExpander::dummy() - } else if it.disabled { - CustomProcMacroExpander::disabled() - } else { - CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId::new( - idx as u32, - )) - }, - ) - }) - .collect()), + Some(Ok(proc_macros)) => Ok({ + let ctx = db.syntax_context(tree_id.file_id()); + proc_macros + .iter() + .enumerate() + .map(|(idx, it)| { + let name = Name::new(&it.name, ctx); + ( + name, + if !db.expand_proc_attr_macros() { + CustomProcMacroExpander::dummy() + } else if it.disabled { + CustomProcMacroExpander::disabled() + } else { + CustomProcMacroExpander::new( + hir_expand::proc_macro::ProcMacroId::new(idx as u32), + ) + }, + ) + }) + .collect() + }), Some(Err(e)) => Err(e.clone().into_boxed_str()), None => Err("No proc-macros present for crate".to_owned().into_boxed_str()), } @@ -291,24 +294,24 @@ impl DefCollector<'_> { let Some(attr_name) = attr.path.as_ident() else { continue }; match () { - () if *attr_name == hir_expand::name![recursion_limit] => { + () if *attr_name == sym::recursion_limit => { if let Some(limit) = attr.string_value() { if let Ok(limit) = limit.parse() { crate_data.recursion_limit = Some(limit); } } } - () if *attr_name == hir_expand::name![crate_type] => { + () if *attr_name == sym::crate_type => { if let Some("proc-macro") = attr.string_value() { self.is_proc_macro = true; } } - () if *attr_name == hir_expand::name![no_core] => crate_data.no_core = true, - () if *attr_name == hir_expand::name![no_std] => crate_data.no_std = true, - () if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") => { + () if *attr_name == sym::no_core => crate_data.no_core = true, + () if *attr_name == sym::no_std => crate_data.no_std = true, + () if *attr_name == sym::rustc_coherence_is_core => { crate_data.rustc_coherence_is_core = true; } - () if *attr_name == hir_expand::name![feature] => { + () if *attr_name == sym::feature => { let features = attr .parse_path_comma_token_tree(self.db.upcast()) .into_iter() @@ -319,13 +322,13 @@ impl DefCollector<'_> { }); crate_data.unstable_features.extend(features); } - () if *attr_name == hir_expand::name![register_attr] => { + () if *attr_name == sym::register_attr => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_attrs.push(ident.text.clone()); cov_mark::hit!(register_attr); } } - () if *attr_name == hir_expand::name![register_tool] => { + () if *attr_name == sym::register_tool => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_tools.push(ident.text.clone()); cov_mark::hit!(register_tool); @@ -535,27 +538,30 @@ impl DefCollector<'_> { } let krate = if self.def_map.data.no_std { - name![core] - } else if self.def_map.extern_prelude().any(|(name, _)| *name == name![std]) { - name![std] + Name::new_symbol_root(sym::core) + } else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std) { + Name::new_symbol_root(sym::std) } else { // If `std` does not exist for some reason, fall back to core. This mostly helps // keep r-a's own tests minimal. - name![core] + Name::new_symbol_root(sym::core) }; let edition = match self.def_map.data.edition { - Edition::Edition2015 => name![rust_2015], - Edition::Edition2018 => name![rust_2018], - Edition::Edition2021 => name![rust_2021], - Edition::Edition2024 => name![rust_2024], + Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015), + Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018), + Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021), + Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024), }; let path_kind = match self.def_map.data.edition { Edition::Edition2015 => PathKind::Plain, _ => PathKind::Abs, }; - let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]); + let path = ModPath::from_segments( + path_kind, + [krate, Name::new_symbol_root(sym::prelude), edition], + ); let (per_ns, _) = self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); @@ -838,7 +844,7 @@ impl DefCollector<'_> { } fn resolve_extern_crate(&self, name: &Name) -> Option { - if *name == name![self] { + if *name == sym::self_ { cov_mark::hit!(extern_crate_self_as); Some(self.def_map.crate_root()) } else { @@ -2136,9 +2142,9 @@ impl ModCollector<'_, '_> { let expander = if attrs.by_key("rustc_builtin_macro").exists() { // `#[rustc_builtin_macro = "builtin_name"]` overrides the `macro_rules!` name. let name; - let name = match attrs.by_key("rustc_builtin_macro").string_value() { - Some(it) => { - name = Name::new_text_dont_use(it.into()); + let name = match attrs.by_key("rustc_builtin_macro").string_value_with_span() { + Some((it, span)) => { + name = Name::new(it, span.ctx); &name } None => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index cee9e055459f5..22397df961066 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -6,9 +6,9 @@ use crate::{lower::LowerCtx, type_ref::ConstRef}; use hir_expand::{ mod_path::resolve_crate_root, - name::{name, AsName}, + name::{AsName, Name}, }; -use intern::Interned; +use intern::{sym, Interned}; use syntax::ast::{self, AstNode, HasGenericArgs, HasTypeBounds}; use crate::{ @@ -60,7 +60,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option { - segments.push(name![Self]); + segments.push(Name::new_symbol_root(sym::Self_)); } ast::PathSegmentKind::Type { type_ref, trait_ref } => { assert!(path.qualifier().is_none()); // this can only occur at the first segment @@ -268,7 +268,7 @@ fn lower_generic_args_from_fn_path( let bindings = if let Some(ret_type) = ret_type { let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty()); Box::new([AssociatedTypeBinding { - name: name![Output], + name: Name::new_symbol_root(sym::Output), args: None, type_ref: Some(type_ref), bounds: Box::default(), @@ -277,7 +277,7 @@ fn lower_generic_args_from_fn_path( // -> () let type_ref = TypeRef::Tuple(Vec::new()); Box::new([AssociatedTypeBinding { - name: name![Output], + name: Name::new_symbol_root(sym::Output), args: None, type_ref: Some(type_ref), bounds: Box::default(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index e5c1f93bbde65..4931418546603 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -2,11 +2,8 @@ use std::{fmt, iter, mem}; use base_db::CrateId; -use hir_expand::{ - name::{name, Name}, - MacroDefId, -}; -use intern::Interned; +use hir_expand::{name::Name, MacroDefId}; +use intern::{sym, Interned}; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; use triomphe::Arc; @@ -197,12 +194,12 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if first_name == &name![Self] { + if *first_name == sym::Self_ { return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); } } &Scope::AdtScope(adt) => { - if first_name == &name![Self] { + if *first_name == sym::Self_ { return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); } } @@ -294,7 +291,7 @@ impl Resolver { } }; let n_segments = path.segments().len(); - let tmp = name![self]; + let tmp = Name::new_symbol_root(sym::Self_); let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); if skip_to_mod { @@ -325,7 +322,7 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if first_name == &name![Self] { + if *first_name == sym::Self_ { return Some(ResolveValueResult::ValueNs( ValueNs::ImplSelf(impl_), None, @@ -352,7 +349,7 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if first_name == &name![Self] { + if *first_name == sym::Self_ { return Some(ResolveValueResult::Partial( TypeNs::SelfType(impl_), 1, @@ -361,7 +358,7 @@ impl Resolver { } } Scope::AdtScope(adt) => { - if first_name == &name![Self] { + if *first_name == sym::Self_ { let ty = TypeNs::AdtSelfType(*adt); return Some(ResolveValueResult::Partial(ty, 1, None)); } @@ -425,7 +422,7 @@ impl Resolver { } pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { - if lifetime.name == name::known::STATIC_LIFETIME { + if lifetime.name == sym::tick_static { return Some(LifetimeNs::Static); } @@ -781,10 +778,10 @@ impl Scope { } } Scope::ImplDefScope(i) => { - acc.add(&name![Self], ScopeDef::ImplSelfType(*i)); + acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::ImplSelfType(*i)); } Scope::AdtScope(i) => { - acc.add(&name![Self], ScopeDef::AdtSelfType(*i)); + acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::AdtSelfType(*i)); } Scope::ExprScope(scope) => { if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index db0feb055e198..6afa7a40f123f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -4,7 +4,7 @@ use std::{borrow::Cow, fmt, ops}; use base_db::CrateId; use cfg::CfgExpr; use either::Either; -use intern::Interned; +use intern::{sym, Interned}; use mbe::{syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; @@ -12,6 +12,7 @@ use syntax::unescape; use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use triomphe::ThinArc; +use crate::name::Name; use crate::{ db::ExpandDatabase, mod_path::ModPath, @@ -58,7 +59,7 @@ impl RawAttrs { text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))), span, }))), - path: Interned::new(ModPath::from(crate::name!(doc))), + path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))), ctxt: span.ctx, } }), @@ -115,47 +116,47 @@ impl RawAttrs { pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs { let has_cfg_attrs = self .iter() - .any(|attr| attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr])); + .any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr)); if !has_cfg_attrs { return self; } let crate_graph = db.crate_graph(); - let new_attrs = - self.iter() - .flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } + let new_attrs = self + .iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; - - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( - |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), - ); - - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); - - attrs.collect() - } - }) - .collect::>(); + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; + + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts + .enumerate() + .take(1 << AttrId::CFG_ATTR_BITS) + .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); + + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); + + attrs.collect() + } + }) + .collect::>(); let entries = if new_attrs.is_empty() { None } else { @@ -316,6 +317,20 @@ impl Attr { } } + /// #[path = "string"] + pub fn string_value_with_span(&self) -> Option<(&str, span::Span)> { + match self.input.as_deref()? { + AttrInput::Literal(it) => match it.text.strip_prefix('r') { + Some(it) => it.trim_matches('#'), + None => it.text.as_str(), + } + .strip_prefix('"')? + .strip_suffix('"') + .zip(Some(it.span)), + _ => None, + } + } + pub fn string_value_unescape(&self) -> Option> { match self.input.as_deref()? { AttrInput::Literal(it) => match it.text.strip_prefix('r') { @@ -369,7 +384,7 @@ impl Attr { } pub fn cfg(&self) -> Option { - if *self.path.as_ident()? == crate::name![cfg] { + if *self.path.as_ident()? == sym::cfg { self.token_tree_value().map(CfgExpr::parse) } else { None diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs index 2e115f479327e..b9afc666f7528 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs @@ -1,4 +1,5 @@ //! Builtin attributes. +use intern::sym; use span::{MacroCallId, Span}; use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind}; @@ -19,7 +20,7 @@ macro_rules! register_builtin { fn find_by_name(name: &name::Name) -> Option { match name { - $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )* + $( id if id == &sym::$name => Some(BuiltinAttrExpander::$variant), )* _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index c7cdc5e92200f..269e9f308c2f6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,5 +1,6 @@ //! Builtin derives. +use intern::sym; use itertools::izip; use mbe::DocCommentDesugarMode; use rustc_hash::FxHashSet; @@ -36,7 +37,7 @@ macro_rules! register_builtin { fn find_by_name(name: &name::Name) -> Option { match name { - $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )* + $( id if id == &sym::$trait => Some(BuiltinDeriveExpander::$trait), )* _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 02fd431e4e7ec..e5de5975a2d8b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -3,6 +3,7 @@ use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; +use intern::sym; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; @@ -11,8 +12,7 @@ use syntax::ast::{self, AstToken}; use crate::{ db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, - name::{self, known}, - quote, + name, quote, quote::dollar_crate, tt::{self, DelimSpan}, ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroFileIdExt, @@ -48,8 +48,8 @@ macro_rules! register_builtin { fn find_by_name(ident: &name::Name) -> Option> { match ident { - $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )* - $( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )* + $( id if id == &sym::$name => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )* + $( id if id == &sym::$e_name => Some(Either::Right(EagerExpander::$e_kind)), )* _ => return None, } } @@ -367,8 +367,7 @@ fn panic_expand( let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); - let mac = - if use_panic_2021(db, call_site_span) { known::panic_2021 } else { known::panic_2015 }; + let mac = if use_panic_2021(db, call_site_span) { sym::panic_2021 } else { sym::panic_2015 }; // Expand to a macro call `$crate::panic::panic_{edition}` let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); @@ -397,9 +396,9 @@ fn unreachable_expand( let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = if use_panic_2021(db, call_site_span) { - known::unreachable_2021 + sym::unreachable_2021 } else { - known::unreachable_2015 + sym::unreachable_2015 }; // Expand to a macro call `$crate::panic::panic_{edition}` diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index ad25a1168c4d5..e1fb3953f4d2b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -133,6 +133,15 @@ pub trait ExpandDatabase: SourceDatabase { &self, macro_call: MacroCallId, ) -> Option>>>; + #[salsa::transparent] + fn syntax_context(&self, file: HirFileId) -> SyntaxContextId; +} + +fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId { + match file.repr() { + HirFileIdRepr::FileId(_) => SyntaxContextId::ROOT, + HirFileIdRepr::MacroFile(m) => db.macro_arg(m.macro_call_id).2.ctx, + } } /// This expands the given macro call, but with different arguments. This is diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 29408902f16f8..b920d2127abe8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -2,6 +2,7 @@ use std::sync::OnceLock; use base_db::{CrateId, VersionReq}; +use intern::sym; use mbe::DocCommentDesugarMode; use span::{Edition, MacroCallId, Span, SyntaxContextId}; use stdx::TupleExt; @@ -111,8 +112,10 @@ impl DeclarativeMacroExpander { match &*attrs .iter() .find(|it| { - it.path.as_ident().and_then(|it| it.as_str()) - == Some("rustc_macro_transparency") + it.path + .as_ident() + .map(|it| *it == sym::rustc_macro_transparency) + .unwrap_or(false) })? .token_tree_value()? .token_trees diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 12fdf88a2a873..7ff8b797fa4a6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -8,10 +8,11 @@ use std::{ use crate::{ db::ExpandDatabase, hygiene::{marks_rev, SyntaxContextExt, Transparency}, - name::{known, AsName, Name}, + name::{AsName, Name}, tt, }; use base_db::CrateId; +use intern::sym; use smallvec::SmallVec; use span::SyntaxContextId; use syntax::{ast, AstNode}; @@ -106,10 +107,7 @@ impl ModPath { PathKind::Abs => 0, PathKind::DollarCrate(_) => "$crate".len(), }; - self.segments() - .iter() - .map(|segment| segment.as_str().map_or(0, str::len)) - .fold(base, core::ops::Add::add) + self.segments().iter().map(|segment| segment.as_str().len()).fold(base, core::ops::Add::add) } pub fn is_ident(&self) -> bool { @@ -122,8 +120,7 @@ impl ModPath { #[allow(non_snake_case)] pub fn is_Self(&self) -> bool { - self.kind == PathKind::Plain - && matches!(&*self.segments, [name] if *name == known::SELF_TYPE) + self.kind == PathKind::Plain && matches!(&*self.segments, [name] if *name == sym::Self_) } /// If this path is a single identifier, like `foo`, return its name. @@ -265,9 +262,10 @@ fn convert_path( res } } - ast::PathSegmentKind::SelfTypeKw => { - ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) - } + ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments( + PathKind::Plain, + Some(Name::new_symbol(sym::Self_, SyntaxContextId::ROOT)), + ), ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, ast::PathSegmentKind::SuperKw => handle_super_kw(1)?, @@ -323,9 +321,9 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option PathKind::SELF, tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => { let mut deg = 1; - while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leaves.next() { + while let Some(tt::Leaf::Ident(tt::Ident { text, span, .. })) = leaves.next() { if text != "super" { - segments.push(Name::new_text_dont_use(text.clone())); + segments.push(Name::new(text, span.ctx)); break; } deg += 1; @@ -334,13 +332,13 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option PathKind::Crate, tt::Leaf::Ident(ident) => { - segments.push(Name::new_text_dont_use(ident.text.clone())); + segments.push(Name::new(&ident.text, ident.span.ctx)); PathKind::Plain } _ => return None, }; segments.extend(leaves.filter_map(|leaf| match leaf { - ::tt::Leaf::Ident(ident) => Some(Name::new_text_dont_use(ident.text.clone())), + ::tt::Leaf::Ident(ident) => Some(Name::new(&ident.text, ident.span.ctx)), _ => None, })); Some(ModPath { kind, segments }) @@ -385,6 +383,8 @@ macro_rules! __known_path { (core::ops::RangeInclusive) => {}; (core::future::Future) => {}; (core::future::IntoFuture) => {}; + (core::fmt::Debug) => {}; + (std::fmt::format) => {}; (core::ops::Try) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") @@ -396,7 +396,7 @@ macro_rules! __path { ($start:ident $(:: $seg:ident)*) => ({ $crate::__known_path!($start $(:: $seg)*); $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![ - $crate::mod_path::__name![$start], $($crate::mod_path::__name![$seg],)* + $crate::name::Name::new_symbol_root(intern::sym::$start), $($crate::name::Name::new_symbol_root(intern::sym::$seg),)* ]) }); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index fe754bc82494a..da0adbef16342 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -2,6 +2,8 @@ use std::fmt; +use intern::{sym::MISSING_NAME, Symbol}; +use span::SyntaxContextId; use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr}; /// `Name` is a wrapper around string, which is used in hir for both references @@ -11,32 +13,49 @@ use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr}; /// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it /// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the /// name without "r#". -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Name(Repr); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Name { + symbol: Symbol, + ctx: (), +} -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct UnescapedName<'a>(&'a Name); +impl Ord for Name { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.symbol.as_str().cmp(other.symbol.as_str()) + } +} -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -enum Repr { - Text(SmolStr), - TupleField(usize), +impl PartialOrd for Name { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } +impl PartialEq for Name { + fn eq(&self, sym: &Symbol) -> bool { + self.symbol == *sym + } +} + +impl PartialEq for Symbol { + fn eq(&self, name: &Name) -> bool { + *self == name.symbol + } +} + +/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UnescapedName<'a>(&'a Name); + impl UnescapedName<'_> { /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. pub fn to_smol_str(&self) -> SmolStr { - match &self.0 .0 { - Repr::Text(it) => { - if let Some(stripped) = it.strip_prefix("r#") { - SmolStr::new(stripped) - } else { - it.clone() - } - } - Repr::TupleField(it) => SmolStr::new(it.to_string()), + let it = self.0.symbol.as_str(); + if let Some(stripped) = it.strip_prefix("r#") { + SmolStr::new(stripped) + } else { + it.into() } } @@ -50,27 +69,26 @@ impl Name { /// Note: this is private to make creating name from random string hard. /// Hopefully, this should allow us to integrate hygiene cleaner in the /// future, and to switch to interned representation of names. - const fn new_text(text: SmolStr) -> Name { - Name(Repr::Text(text)) + fn new_text(text: &str) -> Name { + Name { symbol: Symbol::intern(text), ctx: () } } - // FIXME: See above, unfortunately some places really need this right now - #[doc(hidden)] - pub const fn new_text_dont_use(text: SmolStr) -> Name { - Name(Repr::Text(text)) + pub fn new(text: &str, ctx: SyntaxContextId) -> Name { + _ = ctx; + Name { symbol: Symbol::intern(text), ctx: () } } pub fn new_tuple_field(idx: usize) -> Name { - Name(Repr::TupleField(idx)) + Name { symbol: Symbol::intern(&idx.to_string()), ctx: () } } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Self::new_text(lt.text().into()) + Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () } } /// Shortcut to create a name from a string literal. - const fn new_static(text: &'static str) -> Name { - Name::new_text(SmolStr::new_static(text)) + fn new_ref(text: &str) -> Name { + Name { symbol: Symbol::intern(text), ctx: () } } /// Resolve a name from the text of token. @@ -78,14 +96,14 @@ impl Name { match raw_text.strip_prefix("r#") { // When `raw_text` starts with "r#" but the name does not coincide with any // keyword, we never need the prefix so we strip it. - Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), + Some(text) if !is_raw_identifier(text) => Name::new_ref(text), // Keywords (in the current edition) *can* be used as a name in earlier editions of // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their // escaped form. None if is_raw_identifier(raw_text) => { - Name::new_text(format_smolstr!("r#{}", raw_text)) + Name::new_text(&format_smolstr!("r#{}", raw_text)) } - _ => Name::new_text(raw_text.into()), + _ => Name::new_text(raw_text), } } @@ -99,7 +117,7 @@ impl Name { /// name is equal only to itself. It's not clear how to implement this in /// salsa though, so we punt on that bit for a moment. pub const fn missing() -> Name { - Name::new_static("[missing name]") + Name { symbol: MISSING_NAME, ctx: () } } /// Returns true if this is a fake name for things missing in the source code. See @@ -115,41 +133,25 @@ impl Name { /// creating desugared locals and labels. The caller is responsible for picking an index /// that is stable across re-executions pub fn generate_new_name(idx: usize) -> Name { - Name::new_text(format_smolstr!("{idx}")) + Name::new_text(&format_smolstr!("{idx}")) } /// Returns the tuple index this name represents if it is a tuple field. pub fn as_tuple_index(&self) -> Option { - match self.0 { - Repr::TupleField(idx) => Some(idx), - _ => None, - } + self.symbol.as_str().parse().ok() } /// Returns the text this name represents if it isn't a tuple field. - pub fn as_text(&self) -> Option { - match &self.0 { - Repr::Text(it) => Some(it.clone()), - _ => None, - } - } - - /// Returns the text this name represents if it isn't a tuple field. - pub fn as_str(&self) -> Option<&str> { - match &self.0 { - Repr::Text(it) => Some(it), - _ => None, - } + pub fn as_str(&self) -> &str { + self.symbol.as_str() } + // FIXME: Remove this /// Returns the textual representation of this name as a [`SmolStr`]. /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in /// the general case. pub fn to_smol_str(&self) -> SmolStr { - match &self.0 { - Repr::Text(it) => it.clone(), - Repr::TupleField(it) => SmolStr::new(it.to_string()), - } + self.symbol.as_str().into() } pub fn unescaped(&self) -> UnescapedName<'_> { @@ -157,16 +159,27 @@ impl Name { } pub fn is_escaped(&self) -> bool { - match &self.0 { - Repr::Text(it) => it.starts_with("r#"), - Repr::TupleField(_) => false, - } + self.symbol.as_str().starts_with("r#") } pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { _ = db; Display { name: self } } + + pub fn symbol(&self) -> &Symbol { + &self.symbol + } + + pub const fn new_symbol(doc: Symbol, ctx: SyntaxContextId) -> Self { + _ = ctx; + Self { symbol: doc, ctx: () } + } + + // FIXME: This needs to go once we have hygiene + pub const fn new_symbol_root(doc: Symbol) -> Self { + Self { symbol: doc, ctx: () } + } } struct Display<'a> { @@ -175,10 +188,7 @@ struct Display<'a> { impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.name.0 { - Repr::Text(text) => fmt::Display::fmt(&text, f), - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } + fmt::Display::fmt(self.name.symbol.as_str(), f) } } @@ -188,13 +198,9 @@ struct UnescapedDisplay<'a> { impl fmt::Display for UnescapedDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.name.0 .0 { - Repr::Text(text) => { - let text = text.strip_prefix("r#").unwrap_or(text); - fmt::Display::fmt(&text, f) - } - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } + let symbol = &self.name.0.symbol.as_str(); + let text = symbol.strip_prefix("r#").unwrap_or(symbol); + fmt::Display::fmt(&text, f) } } @@ -246,251 +252,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(SmolStr::new(&*self.name)) + Name::new_text(&self.name) } } - -pub mod known { - macro_rules! known_names { - ($($ident:ident),* $(,)?) => { - $( - #[allow(bad_style)] - pub const $ident: super::Name = - super::Name::new_static(stringify!($ident)); - )* - }; - } - - known_names!( - // Primitives - isize, - i8, - i16, - i32, - i64, - i128, - usize, - u8, - u16, - u32, - u64, - u128, - f16, - f32, - f64, - f128, - bool, - char, - str, - // Special names - macro_rules, - doc, - cfg, - cfg_attr, - register_attr, - register_tool, - // Components of known path (value or mod name) - std, - core, - alloc, - iter, - ops, - fmt, - future, - result, - string, - boxed, - option, - prelude, - rust_2015, - rust_2018, - rust_2021, - rust_2024, - v1, - new_display, - new_debug, - new_lower_exp, - new_upper_exp, - new_octal, - new_pointer, - new_binary, - new_lower_hex, - new_upper_hex, - from_usize, - panic_2015, - panic_2021, - unreachable_2015, - unreachable_2021, - // Components of known path (type name) - Iterator, - IntoIterator, - Item, - IntoIter, - Try, - Ok, - Future, - IntoFuture, - Result, - Option, - Output, - Target, - Box, - RangeFrom, - RangeFull, - RangeInclusive, - RangeToInclusive, - RangeTo, - Range, - String, - Neg, - Not, - None, - Index, - Left, - Right, - Center, - Unknown, - Is, - Param, - Implied, - // Components of known path (function name) - filter_map, - next, - iter_mut, - len, - is_empty, - as_str, - new, - new_v1_formatted, - none, - // Builtin macros - asm, - assert, - column, - compile_error, - concat_idents, - concat_bytes, - concat, - const_format_args, - core_panic, - env, - file, - format, - format_args_nl, - format_args, - global_asm, - include_bytes, - include_str, - include, - line, - llvm_asm, - log_syntax, - module_path, - option_env, - quote, - std_panic, - stringify, - trace_macros, - unreachable, - // Builtin derives - Copy, - Clone, - Default, - Debug, - Hash, - Ord, - PartialOrd, - Eq, - PartialEq, - // Builtin attributes - bench, - cfg_accessible, - cfg_eval, - crate_type, - derive, - derive_const, - global_allocator, - no_core, - no_std, - test, - test_case, - recursion_limit, - feature, - // known methods of lang items - call_once, - call_mut, - call, - eq, - ne, - ge, - gt, - le, - lt, - // known fields of lang items - pieces, - // lang items - add_assign, - add, - bitand_assign, - bitand, - bitor_assign, - bitor, - bitxor_assign, - bitxor, - branch, - deref_mut, - deref, - div_assign, - div, - drop, - fn_mut, - fn_once, - future_trait, - index, - index_mut, - into_future, - mul_assign, - mul, - neg, - not, - owned_box, - partial_ord, - poll, - r#fn, - rem_assign, - rem, - shl_assign, - shl, - shr_assign, - shr, - sub_assign, - sub, - unsafe_cell, - va_list - ); - - // self/Self cannot be used as an identifier - pub const SELF_PARAM: super::Name = super::Name::new_static("self"); - pub const SELF_TYPE: super::Name = super::Name::new_static("Self"); - - pub const STATIC_LIFETIME: super::Name = super::Name::new_static("'static"); - pub const DOLLAR_CRATE: super::Name = super::Name::new_static("$crate"); - - #[macro_export] - macro_rules! name { - (self) => { - $crate::name::known::SELF_PARAM - }; - (Self) => { - $crate::name::known::SELF_TYPE - }; - ('static) => { - $crate::name::known::STATIC_LIFETIME - }; - ($ident:ident) => { - $crate::name::known::$ident - }; - } -} - -pub use crate::name; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index 8f1e32321e147..f4fc3b7b3e2da 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -1,6 +1,7 @@ //! A simplified version of quote-crate like quasi quote macro #![allow(clippy::crate_in_macro_def)] +use intern::Symbol; use span::Span; use syntax::format_smolstr; @@ -219,6 +220,7 @@ impl_to_to_tokentrees! { span: &str => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}}; span: String => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}}; span: Name => self { crate::tt::Ident{text: self.to_smol_str(), span}}; + span: Symbol => self { crate::tt::Ident{text: self.as_str().into(), span}}; } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index b706cef0b3a28..736379a3d8730 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -5,7 +5,8 @@ use chalk_ir::cast::Cast; use hir_def::lang_item::LangItem; -use hir_expand::name::name; +use hir_expand::name::Name; +use intern::sym; use limit::Limit; use triomphe::Arc; @@ -151,7 +152,8 @@ pub(crate) fn deref_by_trait( let deref_trait = db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; - let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?; + let target = + db.trait_data(deref_trait).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; let projection = { let b = TyBuilder::subst_for_def(db, deref_trait, None); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 3ac8cbaaf8b70..868827b2b6603 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -3,6 +3,8 @@ use core::ops; use std::{iter, ops::ControlFlow, sync::Arc}; +use hir_expand::name::Name; +use intern::sym; use tracing::debug; use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds}; @@ -16,7 +18,6 @@ use hir_def::{ AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, VariantId, }; -use hir_expand::name::name; use crate::{ db::{HirDatabase, InternedCoroutine}, @@ -293,8 +294,10 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { .lang_item(self.krate, LangItem::Future) .and_then(|item| item.as_trait()) .and_then(|trait_| { - let alias = - self.db.trait_data(trait_).associated_type_by_name(&name![Output])?; + let alias = self + .db + .trait_data(trait_) + .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; Some((trait_, alias)) }) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 15ecf9aafcfc9..1df801f3a4fd5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -247,7 +247,7 @@ impl<'a> DeclValidator<'a> { // Check the module name. let Some(module_name) = module_id.name(self.db.upcast()) else { return }; let Some(module_name_replacement) = - module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement { + to_lower_snake_case(module_name.as_str()).map(|new_name| Replacement { current_name: module_name, suggested_text: new_name, expected_case: CaseType::LowerSnakeCase, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index c28ab2e98afb1..048bdd2c38bce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -8,7 +8,7 @@ use either::Either; use hir_def::lang_item::LangItem; use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule}; use hir_def::{ItemContainerId, Lookup}; -use hir_expand::name; +use intern::sym; use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_pattern_analysis::constructor::Constructor; @@ -423,7 +423,7 @@ impl FilterMapNextChecker { ItemContainerId::TraitId(iterator_trait_id) => { let iterator_trait_items = &db.trait_data(iterator_trait_id).items; iterator_trait_items.iter().find_map(|(name, it)| match it { - &AssocItemId::FunctionId(id) if *name == name![filter_map] => Some(id), + &AssocItemId::FunctionId(id) if *name == sym::filter_map => Some(id), _ => None, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index a9a5d829f5f91..2a644de1fbecc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -25,7 +25,7 @@ use hir_def::{ ModuleId, TraitId, }; use hir_expand::name::Name; -use intern::{Internable, Interned}; +use intern::{sym, Internable, Interned}; use itertools::Itertools; use la_arena::ArenaMap; use rustc_apfloat::{ @@ -1171,7 +1171,8 @@ impl HirDisplay for Ty { .lang_item(body.module(db.upcast()).krate(), LangItem::Future) .and_then(LangItemTarget::as_trait); let output = future_trait.and_then(|t| { - db.trait_data(t).associated_type_by_name(&hir_expand::name!(Output)) + db.trait_data(t) + .associated_type_by_name(&Name::new_symbol_root(sym::Output)) }); write!(f, "impl ")?; if let Some(t) = future_trait { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 66ee02d74d9a0..928a3f5e493c7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -46,8 +46,9 @@ use hir_def::{ AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; -use hir_expand::name::{name, Name}; +use hir_expand::name::Name; use indexmap::IndexSet; +use intern::sym; use la_arena::{ArenaMap, Entry}; use once_cell::unsync::OnceCell; use rustc_hash::{FxHashMap, FxHashSet}; @@ -1424,7 +1425,7 @@ impl<'a> InferenceContext<'a> { } fn resolve_output_on(&self, trait_: TraitId) -> Option { - self.db.trait_data(trait_).associated_type_by_name(&name![Output]) + self.db.trait_data(trait_).associated_type_by_name(&Name::new_symbol_root(sym::Output)) } fn resolve_lang_trait(&self, lang: LangItem) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index b7c7b6654536a..f54081a276c8d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -15,7 +15,8 @@ use hir_def::{ resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, }; -use hir_expand::name; +use hir_expand::name::Name; +use intern::sym; use rustc_hash::FxHashMap; use smallvec::SmallVec; use stdx::never; @@ -268,9 +269,7 @@ impl CapturedItem { } let variant_data = f.parent.variant_data(db.upcast()); let field = match &*variant_data { - VariantData::Record(fields) => { - fields[f.local_id].name.as_str().unwrap_or("[missing field]").to_owned() - } + VariantData::Record(fields) => fields[f.local_id].name.as_str().to_owned(), VariantData::Tuple(fields) => fields .iter() .position(|it| it.0 == f.local_id) @@ -621,8 +620,10 @@ impl InferenceContext<'_> { if let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) { - if let Some(deref_fn) = - self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { break 'b deref_fn == f; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 7a0f7872a649d..a0c3f48642e3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -15,7 +15,8 @@ use hir_def::{ path::{GenericArgs, Path}, BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; -use hir_expand::name::{name, Name}; +use hir_expand::name::Name; +use intern::sym; use stdx::always; use syntax::ast::RangeOp; @@ -646,8 +647,10 @@ impl InferenceContext<'_> { match op { UnaryOp::Deref => { if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) { - if let Some(deref_fn) = - self.db.trait_data(deref_trait).method_by_name(&name![deref]) + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&Name::new_symbol_root(sym::deref)) { // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that // the mutability is not wrong, and will be fixed in `self.infer_mut`). @@ -785,8 +788,10 @@ impl InferenceContext<'_> { // mutability will be fixed up in `InferenceContext::infer_mut`; adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone())); self.write_expr_adj(*base, adj); - if let Some(func) = - self.db.trait_data(index_trait).method_by_name(&name!(index)) + if let Some(func) = self + .db + .trait_data(index_trait) + .method_by_name(&Name::new_symbol_root(sym::index)) { let substs = TyBuilder::subst_for_def(self.db, index_trait, None) .push(self_ty.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 00e5eac229fb6..ca8996fb89e77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -6,7 +6,8 @@ use hir_def::{ hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, lang_item::LangItem, }; -use hir_expand::name; +use hir_expand::name::Name; +use intern::sym; use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref}; @@ -108,8 +109,10 @@ impl InferenceContext<'_> { .lang_item(self.table.trait_env.krate, LangItem::IndexMut) .and_then(|l| l.as_trait()) { - if let Some(index_fn) = - self.db.trait_data(index_trait).method_by_name(&name![index_mut]) + if let Some(index_fn) = self + .db + .trait_data(index_trait) + .method_by_name(&Name::new_symbol_root(sym::index_mut)) { *f = index_fn; let base_adjustments = self @@ -139,8 +142,10 @@ impl InferenceContext<'_> { .lang_item(self.table.trait_env.krate, LangItem::DerefMut) .and_then(|l| l.as_trait()) { - if let Some(deref_fn) = - self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { *f = deref_fn; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 490ecfd7fa3c3..67a0ec60f19d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -7,6 +7,7 @@ use hir_def::{ AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup, }; use hir_expand::name::Name; +use intern::sym; use stdx::never; use crate::{ @@ -227,7 +228,7 @@ impl InferenceContext<'_> { Path::LangItem(..) => ( PathSegment { name: { - _d = hir_expand::name::known::Unknown; + _d = Name::new_symbol_root(sym::Unknown); &_d }, args_and_bindings: None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index ed4d55d203773..800b06afbd0b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -9,7 +9,8 @@ use chalk_ir::{ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; -use hir_expand::name; +use hir_expand::name::Name; +use intern::sym; use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; @@ -781,7 +782,8 @@ impl<'a> InferenceTable<'a> { let krate = self.trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let trait_data = self.db.trait_data(fn_once_trait); - let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?; + let output_assoc_type = + trait_data.associated_type_by_name(&Name::new_symbol_root(sym::Output))?; let mut arg_tys = Vec::with_capacity(num_args); let arg_ty = TyBuilder::tuple(num_args) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index 85ed46b96321a..69ff03eb49ece 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -2,6 +2,7 @@ use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId}; use hir_expand::name::Name; +use intern::sym; use crate::db::HirDatabase; @@ -16,48 +17,47 @@ pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool { } pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> { - use hir_expand::name; use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering}; Some(match op { BinaryOp::LogicOp(_) => return None, BinaryOp::ArithOp(aop) => match aop { - ArithOp::Add => (name![add], LangItem::Add), - ArithOp::Mul => (name![mul], LangItem::Mul), - ArithOp::Sub => (name![sub], LangItem::Sub), - ArithOp::Div => (name![div], LangItem::Div), - ArithOp::Rem => (name![rem], LangItem::Rem), - ArithOp::Shl => (name![shl], LangItem::Shl), - ArithOp::Shr => (name![shr], LangItem::Shr), - ArithOp::BitXor => (name![bitxor], LangItem::BitXor), - ArithOp::BitOr => (name![bitor], LangItem::BitOr), - ArithOp::BitAnd => (name![bitand], LangItem::BitAnd), + ArithOp::Add => (Name::new_symbol_root(sym::add), LangItem::Add), + ArithOp::Mul => (Name::new_symbol_root(sym::mul), LangItem::Mul), + ArithOp::Sub => (Name::new_symbol_root(sym::sub), LangItem::Sub), + ArithOp::Div => (Name::new_symbol_root(sym::div), LangItem::Div), + ArithOp::Rem => (Name::new_symbol_root(sym::rem), LangItem::Rem), + ArithOp::Shl => (Name::new_symbol_root(sym::shl), LangItem::Shl), + ArithOp::Shr => (Name::new_symbol_root(sym::shr), LangItem::Shr), + ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor), LangItem::BitXor), + ArithOp::BitOr => (Name::new_symbol_root(sym::bitor), LangItem::BitOr), + ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand), LangItem::BitAnd), }, BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (name![add_assign], LangItem::AddAssign), - ArithOp::Mul => (name![mul_assign], LangItem::MulAssign), - ArithOp::Sub => (name![sub_assign], LangItem::SubAssign), - ArithOp::Div => (name![div_assign], LangItem::DivAssign), - ArithOp::Rem => (name![rem_assign], LangItem::RemAssign), - ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign), - ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign), - ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign), - ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign), - ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign), + ArithOp::Add => (Name::new_symbol_root(sym::add_assign), LangItem::AddAssign), + ArithOp::Mul => (Name::new_symbol_root(sym::mul_assign), LangItem::MulAssign), + ArithOp::Sub => (Name::new_symbol_root(sym::sub_assign), LangItem::SubAssign), + ArithOp::Div => (Name::new_symbol_root(sym::div_assign), LangItem::DivAssign), + ArithOp::Rem => (Name::new_symbol_root(sym::rem_assign), LangItem::RemAssign), + ArithOp::Shl => (Name::new_symbol_root(sym::shl_assign), LangItem::ShlAssign), + ArithOp::Shr => (Name::new_symbol_root(sym::shr_assign), LangItem::ShrAssign), + ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor_assign), LangItem::BitXorAssign), + ArithOp::BitOr => (Name::new_symbol_root(sym::bitor_assign), LangItem::BitOrAssign), + ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand_assign), LangItem::BitAndAssign), }, BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq), - CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq), + CmpOp::Eq { negated: false } => (Name::new_symbol_root(sym::eq), LangItem::PartialEq), + CmpOp::Eq { negated: true } => (Name::new_symbol_root(sym::ne), LangItem::PartialEq), CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (name![le], LangItem::PartialOrd) + (Name::new_symbol_root(sym::le), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (name![lt], LangItem::PartialOrd) + (Name::new_symbol_root(sym::lt), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (name![ge], LangItem::PartialOrd) + (Name::new_symbol_root(sym::ge), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (name![gt], LangItem::PartialOrd) + (Name::new_symbol_root(sym::gt), LangItem::PartialOrd) } }, BinaryOp::Assignment { op: None } => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index bd650869bb386..60447ac582030 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -61,7 +61,8 @@ use chalk_ir::{ }; use either::Either; use hir_def::{hir::ExprId, type_ref::Rawness, CallableDefId, GeneralConstId, TypeOrConstParamId}; -use hir_expand::name; +use hir_expand::name::Name; +use intern::sym; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -894,7 +895,9 @@ pub fn callable_sig_from_fn_trait( ) -> Option<(FnTrait, CallableSig)> { let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; - let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + let output_assoc_type = db + .trait_data(fn_once_trait) + .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; let mut table = InferenceTable::new(db, trait_env.clone()); let b = TyBuilder::trait_ref(db, fn_once_trait); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 2d9c221b732b0..982080c5ff5fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -14,8 +14,8 @@ use hir_def::{ AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, VariantId, }; -use hir_expand::{mod_path::ModPath, HirFileIdExt, InFile}; -use intern::Interned; +use hir_expand::{mod_path::path, name::Name, HirFileIdExt, InFile}; +use intern::{sym, Interned}; use la_arena::ArenaMap; use rustc_abi::TargetDataLayout; use rustc_apfloat::{ @@ -35,7 +35,7 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_const}, - name, static_lifetime, + static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstScalar, FnDefId, Interner, MemoryMap, @@ -631,15 +631,19 @@ impl Evaluator<'_> { cached_fn_trait_func: db .lang_item(crate_id, LangItem::Fn) .and_then(|x| x.as_trait()) - .and_then(|x| db.trait_data(x).method_by_name(&name![call])), + .and_then(|x| db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call))), cached_fn_mut_trait_func: db .lang_item(crate_id, LangItem::FnMut) .and_then(|x| x.as_trait()) - .and_then(|x| db.trait_data(x).method_by_name(&name![call_mut])), + .and_then(|x| { + db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_mut)) + }), cached_fn_once_trait_func: db .lang_item(crate_id, LangItem::FnOnce) .and_then(|x| x.as_trait()) - .and_then(|x| db.trait_data(x).method_by_name(&name![call_once])), + .and_then(|x| { + db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_once)) + }), }) } @@ -2633,10 +2637,7 @@ impl Evaluator<'_> { let static_data = self.db.static_data(st); let result = if !static_data.is_extern { let konst = self.db.const_eval_static(st).map_err(|e| { - MirEvalError::ConstEvalError( - static_data.name.as_str().unwrap_or("_").to_owned(), - Box::new(e), - ) + MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e)) })?; self.allocate_const_in_heap(locals, &konst)? } else { @@ -2693,7 +2694,7 @@ impl Evaluator<'_> { ) -> Result<()> { let Some(drop_fn) = (|| { let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?; - self.db.trait_data(drop_trait).method_by_name(&name![drop]) + self.db.trait_data(drop_trait).method_by_name(&Name::new_symbol_root(sym::drop)) })() else { // in some tests we don't have drop trait in minicore, and // we can ignore drop in them. @@ -2797,14 +2798,13 @@ pub fn render_const_using_debug_impl( let resolver = owner.resolver(db.upcast()); let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![core], name![fmt], name![Debug]], - )), + &hir_def::path::Path::from_known_path_with_no_generic(path![core::fmt::Debug]), ) else { not_supported!("core::fmt::Debug not found"); }; - let Some(debug_fmt_fn) = db.trait_data(debug_trait).method_by_name(&name![fmt]) else { + let Some(debug_fmt_fn) = + db.trait_data(debug_trait).method_by_name(&Name::new_symbol_root(sym::fmt)) + else { not_supported!("core::fmt::Debug::fmt not found"); }; // a1 = &[""] @@ -2829,10 +2829,7 @@ pub fn render_const_using_debug_impl( evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?; let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]], - )), + &hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]), ) else { not_supported!("std::fmt::format not found"); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index ce22e9d2c2cb8..67e102b1ed183 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -8,12 +8,14 @@ use hir_def::{ builtin_type::{BuiltinInt, BuiltinUint}, resolver::HasResolver, }; +use hir_expand::name::Name; +use intern::sym; use crate::{ error_lifetime, mir::eval::{ - name, pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, - HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, + pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, + Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, }, @@ -64,7 +66,7 @@ impl Evaluator<'_> { }; if is_intrinsic { self.exec_intrinsic( - function_data.name.as_text().unwrap_or_default().as_str(), + function_data.name.as_str(), args, generic_args, destination, @@ -86,7 +88,7 @@ impl Evaluator<'_> { }; if is_platform_intrinsic { self.exec_platform_intrinsic( - function_data.name.as_text().unwrap_or_default().as_str(), + function_data.name.as_str(), args, generic_args, destination, @@ -104,7 +106,7 @@ impl Evaluator<'_> { }; if is_extern_c { self.exec_extern_c( - function_data.name.as_text().unwrap_or_default().as_str(), + function_data.name.as_str(), args, generic_args, destination, @@ -117,7 +119,7 @@ impl Evaluator<'_> { .attrs .iter() .filter_map(|it| it.path().as_ident()) - .filter_map(|it| it.as_str()) + .map(|it| it.as_str()) .find(|it| { [ "rustc_allocator", @@ -1274,10 +1276,11 @@ impl Evaluator<'_> { args.push(IntervalAndTy::new(addr, field, self, locals)?); } if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) { - if let Some(def) = target - .as_trait() - .and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once])) - { + if let Some(def) = target.as_trait().and_then(|it| { + self.db + .trait_data(it) + .method_by_name(&Name::new_symbol_root(sym::call_once)) + }) { self.exec_fn_trait( def, &args, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1a0a1b780a16e..1b8c772d80e32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1113,9 +1113,9 @@ impl<'ctx> MirLowerCtx<'ctx> { .iter() .map(|it| { let o = match it.1.name.as_str() { - Some("start") => lp.take(), - Some("end") => rp.take(), - Some("exhausted") => { + "start" => lp.take(), + "end" => rp.take(), + "exhausted" => { Some(Operand::from_bytes(Box::new([0]), TyBuilder::bool())) } _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 4ad00909e4127..5e8a2dfb735f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -4,7 +4,7 @@ use crate::mir::MutBorrowKind; use super::*; use hir_def::FunctionId; -use hir_expand::name; +use intern::sym; macro_rules! not_supported { ($it: expr) => { @@ -192,7 +192,7 @@ impl MirLowerCtx<'_> { if let Some(deref_fn) = self .db .trait_data(deref_trait) - .method_by_name(&name![deref_mut]) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { break 'b deref_fn == f; } @@ -324,12 +324,17 @@ impl MirLowerCtx<'_> { mutability: bool, ) -> Result> { let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { - (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared) + ( + Mutability::Not, + LangItem::Deref, + Name::new_symbol_root(sym::deref), + BorrowKind::Shared, + ) } else { ( Mutability::Mut, LangItem::DerefMut, - name![deref_mut], + Name::new_symbol_root(sym::deref_mut), BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 4283a94657b9f..ce9ffa284c904 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -12,7 +12,8 @@ use hir_def::{ lang_item::{LangItem, LangItemTarget}, BlockId, TraitId, }; -use hir_expand::name::{name, Name}; +use hir_expand::name::Name; +use intern::sym; use stdx::panic_context; use triomphe::Arc; @@ -256,9 +257,9 @@ impl FnTrait { pub fn method_name(self) -> Name { match self { - FnTrait::FnOnce => name!(call_once), - FnTrait::FnMut => name!(call_mut), - FnTrait::Fn => name!(call), + FnTrait::FnOnce => Name::new_symbol_root(sym::call_once), + FnTrait::FnMut => Name::new_symbol_root(sym::call_mut), + FnTrait::Fn => Name::new_symbol_root(sym::call), } } diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 7b3ff7b06458b..af60c233e5519 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -12,6 +12,7 @@ use hir_def::{ }; use hir_expand::{mod_path::PathKind, name::Name}; use hir_ty::{db::HirDatabase, method_resolution}; +use span::SyntaxContextId; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -328,7 +329,7 @@ fn doc_modpath_from_str(link: &str) -> Option { let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() { Ok(idx) => Name::new_tuple_field(idx), Err(_) => { - Name::new_text_dont_use(segment.split_once('<').map_or(segment, |it| it.0).into()) + Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT) } }); Some(ModPath::from_segments(kind, parts)) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 016f3418517f9..f90656c75f590 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -58,7 +58,7 @@ use hir_def::{ TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{ - attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult, + attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult, }; use hir_ty::{ all_super_traits, autoderef, check_orphan_rules, @@ -131,7 +131,7 @@ pub use { change::ChangeWithProcMacros, hygiene::{marks_rev, SyntaxContextExt}, inert_attr_macro::AttributeTemplate, - name::{known, Name}, + name::Name, proc_macro::ProcMacros, tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId, MacroFileIdExt, @@ -145,6 +145,7 @@ pub use { }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, + intern::{sym, Symbol}, }; // These are negative re-exports: pub using these names is forbidden, they @@ -1826,7 +1827,7 @@ impl DefWithBody { continue; } let mut need_mut = &mol[local]; - if body[binding_id].name.as_str() == Some("self") + if body[binding_id].name == sym::self_ && need_mut == &mir::MutabilityReason::Unused { need_mut = &mir::MutabilityReason::Not; @@ -1836,7 +1837,7 @@ impl DefWithBody { match (need_mut, is_mut) { (mir::MutabilityReason::Unused, _) => { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); + let should_ignore = body[binding_id].name.as_str().starts_with('_'); if !should_ignore { acc.push(UnusedVariable { local }.into()) } @@ -1866,7 +1867,7 @@ impl DefWithBody { } (mir::MutabilityReason::Not, true) => { if !infer.mutated_bindings_in_closure.contains(&binding_id) { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); + let should_ignore = body[binding_id].name.as_str().starts_with('_'); if !should_ignore { acc.push(UnusedMut { local }.into()) } @@ -2588,7 +2589,7 @@ pub struct StaticLifetime; impl StaticLifetime { pub fn name(self) -> Name { - known::STATIC_LIFETIME + Name::new_symbol_root(sym::tick_static) } } @@ -3248,7 +3249,7 @@ impl Local { } pub fn is_self(self, db: &dyn HirDatabase) -> bool { - self.name(db) == name![self] + self.name(db) == sym::self_ } pub fn is_mut(self, db: &dyn HirDatabase) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 81c57f6caeb63..45401702e9f00 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -25,11 +25,8 @@ use hir_def::{ }; use hir_expand::{ mod_path::path, + name::{AsName, Name}, HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt, - { - name, - name::{AsName, Name}, - }, }; use hir_ty::{ diagnostics::{ @@ -40,6 +37,7 @@ use hir_ty::{ method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext, }; +use intern::sym; use itertools::Itertools; use smallvec::SmallVec; use syntax::{ @@ -368,7 +366,7 @@ impl SourceAnalyzer { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { AssocItem::TypeAlias(alias) - if alias.name(db) == hir_expand::name![IntoFuture] => + if alias.name(db) == Name::new_symbol_root(sym::IntoFuture) => { Some(alias) } @@ -398,14 +396,17 @@ impl SourceAnalyzer { // Since deref kind is inferenced and stored in `InferenceResult.method_resolution`, // use that result to find out which one it is. let (deref_trait, deref) = - self.lang_trait_fn(db, LangItem::Deref, &name![deref])?; + self.lang_trait_fn(db, LangItem::Deref, &Name::new_symbol_root(sym::deref))?; self.infer .as_ref() .and_then(|infer| { let expr = self.expr_id(db, &prefix_expr.clone().into())?; let (func, _) = infer.method_resolution(expr)?; - let (deref_mut_trait, deref_mut) = - self.lang_trait_fn(db, LangItem::DerefMut, &name![deref_mut])?; + let (deref_mut_trait, deref_mut) = self.lang_trait_fn( + db, + LangItem::DerefMut, + &Name::new_symbol_root(sym::deref_mut), + )?; if func == deref_mut { Some((deref_mut_trait, deref_mut)) } else { @@ -414,8 +415,12 @@ impl SourceAnalyzer { }) .unwrap_or((deref_trait, deref)) } - ast::UnaryOp::Not => self.lang_trait_fn(db, LangItem::Not, &name![not])?, - ast::UnaryOp::Neg => self.lang_trait_fn(db, LangItem::Neg, &name![neg])?, + ast::UnaryOp::Not => { + self.lang_trait_fn(db, LangItem::Not, &Name::new_symbol_root(sym::not))? + } + ast::UnaryOp::Neg => { + self.lang_trait_fn(db, LangItem::Neg, &Name::new_symbol_root(sym::neg))? + } }; let ty = self.ty_of_expr(db, &prefix_expr.expr()?)?; @@ -435,15 +440,19 @@ impl SourceAnalyzer { let base_ty = self.ty_of_expr(db, &index_expr.base()?)?; let index_ty = self.ty_of_expr(db, &index_expr.index()?)?; - let (index_trait, index_fn) = self.lang_trait_fn(db, LangItem::Index, &name![index])?; + let (index_trait, index_fn) = + self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index))?; let (op_trait, op_fn) = self .infer .as_ref() .and_then(|infer| { let expr = self.expr_id(db, &index_expr.clone().into())?; let (func, _) = infer.method_resolution(expr)?; - let (index_mut_trait, index_mut_fn) = - self.lang_trait_fn(db, LangItem::IndexMut, &name![index_mut])?; + let (index_mut_trait, index_mut_fn) = self.lang_trait_fn( + db, + LangItem::IndexMut, + &Name::new_symbol_root(sym::index_mut), + )?; if func == index_mut_fn { Some((index_mut_trait, index_mut_fn)) } else { diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 02905ca2ce44d..a6e69d7992b23 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -239,7 +239,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_trait(&mut self, trait_id: TraitId) { let trait_data = self.db.trait_data(trait_id); - self.with_container_name(trait_data.name.as_text(), |s| { + self.with_container_name(Some(trait_data.name.as_str().into()), |s| { for &(_, assoc_item_id) in &trait_data.items { s.push_assoc_item(assoc_item_id); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs index ab25e0167bfe7..c1fcb8e44db84 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -470,7 +470,7 @@ fn add_enum_def( .module() .scope(ctx.db(), Some(*target_module)) .iter() - .any(|(name, _)| name.as_str() == Some("Bool")) + .any(|(name, _)| name.as_str() == "Bool") { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 61b7b41217760..27e39f6f8ab26 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -1,4 +1,4 @@ -use hir::{known, AsAssocItem, Semantics}; +use hir::{sym, AsAssocItem, Semantics}; use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{ @@ -223,7 +223,7 @@ fn option_variants( let fam = FamousDefs(sema, sema.scope(expr)?.krate()); let option_variants = fam.core_option_Option()?.variants(sema.db); match &*option_variants { - &[variant0, variant1] => Some(if variant0.name(sema.db) == known::None { + &[variant0, variant1] => Some(if variant0.name(sema.db) == sym::None { (variant0, variant1) } else { (variant1, variant0) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index db96c8fe40aa5..d85147e84b20c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -1,4 +1,4 @@ -use hir::known; +use hir::{sym, Name}; use ide_db::famous_defs::FamousDefs; use stdx::format_to; use syntax::{ @@ -149,7 +149,11 @@ fn is_ref_and_impls_iter_method( ast::Expr::RefExpr(r) => r, _ => return None, }; - let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter }; + let wanted_method = Name::new_symbol_root(if ref_expr.mut_token().is_some() { + sym::iter_mut + } else { + sym::iter + }); let expr_behind_ref = ref_expr.expr()?; let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted(); let scope = sema.scope(iterable.syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 666e1a1496e03..62bb6dff21e52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -169,8 +169,8 @@ fn get_names_in_scope( let mut names = FxHashSet::default(); scope.process_all_names(&mut |name, scope| { - if let (Some(name), hir::ScopeDef::Local(_)) = (name.as_text(), scope) { - names.insert(name); + if let hir::ScopeDef::Local(_) = scope { + names.insert(name.as_str().into()); } }); Some(names) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index 4d369e705e88e..05210e66ac24d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -1,4 +1,4 @@ -use hir::{known, HasSource, Name}; +use hir::{sym, HasSource, Name}; use syntax::{ ast::{self, HasName}, AstNode, @@ -54,13 +54,13 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext< } let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?; - let len_fn = get_impl_method(ctx, &impl_, &known::len)?; + let len_fn = get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::len))?; if !len_fn.ret_type(ctx.sema.db).is_usize() { cov_mark::hit!(len_fn_different_return_type); return None; } - if get_impl_method(ctx, &impl_, &known::is_empty).is_some() { + if get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::is_empty)).is_some() { cov_mark::hit!(is_empty_already_implemented); return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 8c9fe23bb0bdd..137b8d13171fe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use ast::make; use either::Either; -use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo}; +use hir::{db::HirDatabase, sym, PathResolution, Semantics, TypeInfo}; use ide_db::{ base_db::{FileId, FileRange}, defs::Definition, @@ -430,10 +430,7 @@ fn inline( let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty); - let is_self = param - .name(sema.db) - .and_then(|name| name.as_text()) - .is_some_and(|name| name == "self"); + let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_); if is_self { let mut this_pat = make::ident_pat(false, false, make::name("this")); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 37ea5123a71e6..12d025f07594e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -47,7 +47,7 @@ pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_ None, None, |func| { - let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_lazy) + let valid = func.name(ctx.sema.db).as_str() == &*method_name_lazy && func.num_params(ctx.sema.db) == n_params && { let params = func.params_without_self(ctx.sema.db); @@ -133,7 +133,7 @@ pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<' None, None, |func| { - let valid = func.name(ctx.sema.db).as_str() == Some(method_name_eager) + let valid = func.name(ctx.sema.db).as_str() == method_name_eager && func.num_params(ctx.sema.db) == n_params; valid.then_some(func) }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index ba6ef1921ac63..c67693ea2bb89 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -88,8 +88,8 @@ pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool { let path = attr.path(); (|| { Some( - path.segments().first()?.as_text()?.starts_with("test") - || path.segments().last()?.as_text()?.ends_with("test"), + path.segments().first()?.as_str().starts_with("test") + || path.segments().last()?.as_str().ends_with("test"), ) })() .unwrap_or_default() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 995a4443edff5..4a8ea171805e2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -24,7 +24,7 @@ pub(crate) mod vis; use std::iter; -use hir::{known, HasAttrs, ImportPathConfig, ScopeDef, Variant}; +use hir::{sym, HasAttrs, ImportPathConfig, Name, ScopeDef, Variant}; use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::{ast, SmolStr}; @@ -618,7 +618,7 @@ fn enum_variants_with_paths( let mut process_variant = |variant: Variant| { let self_path = hir::ModPath::from_segments( hir::PathKind::Plain, - iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), + iter::once(Name::new_symbol_root(sym::Self_)).chain(iter::once(variant.name(ctx.db))), ); cb(acc, ctx, variant, self_path); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs index f45f9cba258de..7e3a62405a78c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -18,7 +18,7 @@ pub(super) fn complete_macro_use( for mod_def in krate.root_module().declarations(ctx.db) { if let ModuleDef::Macro(mac) = mod_def { let mac_name = mac.name(ctx.db); - let Some(mac_name) = mac_name.as_str() else { continue }; + let mac_name = mac_name.as_str(); let existing_import = existing_imports .iter() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index a4f092cc498ef..d3290db4ef82b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,5 +1,6 @@ //! Completes references after dot (fields and method calls). +use hir::{sym, Name}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -90,12 +91,14 @@ pub(crate) fn complete_undotted_self( in_breakable: expr_ctx.in_breakable, }, }, - Some(hir::known::SELF_PARAM), + Some(Name::new_symbol_root(sym::self_)), field, &ty, ) }, - |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty), + |acc, field, ty| { + acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_)), field, &ty) + }, true, false, ); @@ -112,7 +115,7 @@ pub(crate) fn complete_undotted_self( }, }, func, - Some(hir::known::SELF_PARAM), + Some(Name::new_symbol_root(sym::self_)), None, ) }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 01f9368aa4ebf..72b36922da0bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,6 @@ //! Completion of names from the current scope in expression position. -use hir::{ImportPathConfig, ScopeDef}; +use hir::{sym, ImportPathConfig, Name, ScopeDef}; use syntax::ast; use crate::{ @@ -190,7 +190,7 @@ pub(crate) fn complete_expr_path( path_ctx, strukt, None, - Some(hir::known::SELF_TYPE), + Some(Name::new_symbol_root(sym::Self_)), ); } } @@ -210,7 +210,12 @@ pub(crate) fn complete_expr_path( acc.add_union_literal(ctx, un, path, None); if complete_self { - acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE)); + acc.add_union_literal( + ctx, + un, + None, + Some(Name::new_symbol_root(sym::Self_)), + ); } } hir::Adt::Enum(e) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 2c6cbf6146a95..541ccf2d198e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs @@ -7,7 +7,7 @@ //! there is no value in lifting these out into the outline module test since they will either not //! show up for normal completions, or they won't show completions other than lifetimes depending //! on the fixture input. -use hir::{known, ScopeDef}; +use hir::{sym, Name, ScopeDef}; use syntax::{ast, TokenText}; use crate::{ @@ -47,7 +47,7 @@ pub(crate) fn complete_lifetime( } }); if param_lifetime.is_none() { - acc.add_lifetime(ctx, known::STATIC_LIFETIME); + acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static)); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 27e9d1d6cfe0c..5fe6cd0e90743 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -55,9 +55,8 @@ pub(crate) fn complete_use_path( if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { continue; } - let is_name_already_imported = name - .as_text() - .map_or(false, |text| already_imported_names.contains(text.as_str())); + let is_name_already_imported = + already_imported_names.contains(name.as_str()); let add_resolution = match def { ScopeDef::Unknown if unknown_is_current(&name) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index a14fe24fa75d7..1e972b9b4ce06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -823,13 +823,13 @@ fn classify_name_ref( for item in trait_.items_with_supertraits(sema.db) { match item { hir::AssocItem::TypeAlias(assoc_ty) => { - if assoc_ty.name(sema.db).as_str()? == arg_name { + if assoc_ty.name(sema.db).as_str() == arg_name { override_location = Some(TypeLocation::AssocTypeEq); return None; } }, hir::AssocItem::Const(const_) => { - if const_.name(sema.db)?.as_str()? == arg_name { + if const_.name(sema.db)?.as_str() == arg_name { override_location = Some(TypeLocation::AssocConstEq); return None; } @@ -867,7 +867,7 @@ fn classify_name_ref( let trait_items = trait_.items_with_supertraits(sema.db); let assoc_ty = trait_items.iter().find_map(|item| match item { hir::AssocItem::TypeAlias(assoc_ty) => { - (assoc_ty.name(sema.db).as_str()? == arg_name) + (assoc_ty.name(sema.db).as_str() == arg_name) .then_some(assoc_ty) }, _ => None, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fe9e2e5268a99..15c20f11863b6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -280,8 +280,7 @@ pub(crate) fn render_expr( let mut snippet_formatter = |ty: &hir::Type| { let arg_name = ty .as_adt() - .and_then(|adt| adt.name(ctx.db).as_text()) - .map(|s| stdx::to_lower_snake_case(s.as_str())) + .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str())) .unwrap_or_else(|| String::from("_")); let res = format!("${{{i}:{arg_name}}}"); i += 1; @@ -290,8 +289,7 @@ pub(crate) fn render_expr( let mut label_formatter = |ty: &hir::Type| { ty.as_adt() - .and_then(|adt| adt.name(ctx.db).as_text()) - .map(|s| stdx::to_lower_snake_case(s.as_str())) + .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str())) .unwrap_or_else(|| String::from("...")) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 48c9d624f63ac..c15e91c404839 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -188,7 +188,7 @@ fn compute_return_type_match( CompletionRelevanceReturnType::Constructor } else if ret_type .as_adt() - .and_then(|adt| adt.name(db).as_str().map(|name| name.ends_with("Builder"))) + .map(|adt| adt.name(db).as_str().ends_with("Builder")) .unwrap_or(false) { // fn([..]) -> [..]Builder @@ -227,11 +227,7 @@ pub(super) fn add_call_parens<'b>( None => { let name = match param.ty().as_adt() { None => "_".to_owned(), - Some(adt) => adt - .name(ctx.db) - .as_text() - .map(|s| to_lower_snake_case(s.as_str())) - .unwrap_or_else(|| "_".to_owned()), + Some(adt) => to_lower_snake_case(adt.name(ctx.db).as_str()), }; f(&format_args!("${{{}:{name}}}", index + offset)) } @@ -264,7 +260,7 @@ pub(super) fn add_call_parens<'b>( fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'static str { if let Some(derefed_ty) = ty.remove_ref() { for (name, local) in ctx.locals.iter() { - if name.as_text().as_deref() == Some(arg) { + if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { if ty.is_mutable_reference() { "&mut " diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index a75a708d95692..dbb2adcd301f5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -144,7 +144,7 @@ impl Definition { Definition::Local(it) => it.name(db), Definition::GenericParam(it) => it.name(db), Definition::Label(it) => it.name(db), - Definition::BuiltinLifetime(StaticLifetime) => hir::known::STATIC_LIFETIME, + Definition::BuiltinLifetime(it) => it.name(), Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 088717a66e590..4814394de6b29 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -389,16 +389,16 @@ fn import_for_item( let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev(); let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it { // segments match, check next one - EitherOrBoth::Both(a, b) if b.as_str() == Some(&**a) => None, + EitherOrBoth::Both(a, b) if b.as_str() == &**a => None, // segments mismatch / qualifier is longer than the path, bail out EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false), // all segments match and we have exhausted the qualifier, proceed EitherOrBoth::Right(_) => Some(true), }; if item_as_assoc.is_none() { - let item_name = item_name(db, original_item)?.as_text()?; + let item_name = item_name(db, original_item)?; let last_segment = import_path_candidate_segments.next()?; - if last_segment.as_str() != Some(&*item_name) { + if *last_segment != item_name { return None; } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index e21d54ccd0e5b..84a388a460b82 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -472,7 +472,7 @@ fn find_trait_for_assoc_item( }); for name in names { - if assoc_item_name.as_str() == name.as_text()?.as_str() { + if assoc_item_name.as_str() == name.as_str() { // It is fine to return the first match because in case of // multiple possibilities, the exact trait must be disambiguated // in the definition of trait being implemented, so this search diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 6a809cb0cefa8..4cdb279a21ca6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,7 +1,7 @@ use either::Either; use hir::{ db::{ExpandDatabase, HirDatabase}, - known, AssocItem, HirDisplay, HirFileIdExt, ImportPathConfig, InFile, Type, + sym, AssocItem, HirDisplay, HirFileIdExt, ImportPathConfig, InFile, Type, }; use ide_db::{ assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search, @@ -210,7 +210,7 @@ fn get_default_constructor( let has_new_func = ty .iterate_assoc_items(ctx.sema.db, krate, |assoc_item| { if let AssocItem::Function(func) = assoc_item { - if func.name(ctx.sema.db) == known::new + if func.name(ctx.sema.db) == sym::new && func.assoc_fn_params(ctx.sema.db).is_empty() { return Some(()); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 41357b59622bf..46c373d8df081 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -76,7 +76,7 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option Some(alias), + hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 51855eeae232e..b60a80a8ac6b5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -46,7 +46,7 @@ pub(crate) fn hints( } let name = param.name(sema.db); - let param_name = name.as_str()?; + let param_name = name.as_str(); let should_hide = { let argument = get_string_representation(&arg)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index 99e2430860794..b27dc60ffafc7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -66,8 +66,11 @@ fn discover_tests_in_module( let mut r = vec![]; for c in module.children(db) { - let module_name = - c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned(); + let module_name = c + .name(db) + .as_ref() + .map(|n| n.as_str().to_owned()) + .unwrap_or_else(|| "[mod without name]".to_owned()); let module_id = format!("{prefix_id}::{module_name}"); let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file); if !module_children.is_empty() { @@ -94,7 +97,7 @@ fn discover_tests_in_module( continue; } let nav = f.try_to_nav(db).map(|r| r.call_site); - let fn_name = f.name(db).as_str().unwrap_or("[function without name]").to_owned(); + let fn_name = f.name(db).as_str().to_owned(); r.push(TestItem { id: format!("{prefix_id}::{fn_name}"), kind: TestItemKind::Function, @@ -153,7 +156,7 @@ fn find_module_id_and_test_parents( let parent = Some(id.clone()); id += "::"; let module_name = &module.name(sema.db); - let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]"); + let module_name = module_name.as_ref().map(|n| n.as_str()).unwrap_or("[mod without name]"); id += module_name; let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site; r.push(TestItem { diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 826447d058dff..ad99c2162cd62 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -64,11 +64,7 @@ enum FieldOrTupleIdx { impl FieldOrTupleIdx { fn name(&self, db: &RootDatabase) -> String { match *self { - FieldOrTupleIdx::Field(f) => f - .name(db) - .as_str() - .map(|s| s.to_owned()) - .unwrap_or_else(|| format!(".{}", f.name(db).as_tuple_index().unwrap())), + FieldOrTupleIdx::Field(f) => f.name(db).as_str().to_owned(), FieldOrTupleIdx::TupleIdx(i) => format!(".{i}"), } } @@ -189,14 +185,7 @@ pub(crate) fn view_memory_layout( | Definition::SelfType(_) => "[ROOT]".to_owned(), // def is an item - def => def - .name(db) - .map(|n| { - n.as_str() - .map(|s| s.to_owned()) - .unwrap_or_else(|| format!(".{}", n.as_tuple_index().unwrap())) - }) - .unwrap_or("[ROOT]".to_owned()), + def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()), }; let typename = ty.display(db).to_string(); diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs index 868d03caff557..58327419f6314 100644 --- a/src/tools/rust-analyzer/crates/intern/src/lib.rs +++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs @@ -21,7 +21,7 @@ type Guard = dashmap::RwLockWriteGuard< >; mod symbol; -pub use self::symbol::{symbols, Symbol}; +pub use self::symbol::{symbols as sym, Symbol}; pub struct Interned { arc: Arc, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 290657a3d3627..1517f516fc15d 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -1,5 +1,5 @@ //! Attempt at flexible symbol interning, allowing to intern and free strings at runtime while also -//! supporting +//! supporting compile time declaration of symbols that will never be freed. use std::{ borrow::Borrow, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index a1cf6e0941466..04bf4dff50c3e 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -11,10 +11,13 @@ use crate::{ Symbol, }; macro_rules! define_symbols { - ($($name:ident),* $(,)?) => { + (@WITH_NAME: $($alias:ident = $value:literal),* $(,)? @PLAIN: $($name:ident),* $(,)?) => { $( pub const $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; )* + $( + pub const $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) }; + )* pub(super) fn prefill() -> DashMap> { @@ -33,12 +36,45 @@ macro_rules! define_symbols { let shard_idx_ = dashmap_.determine_shard(hash_ as usize); dashmap_.shards_mut()[shard_idx_].get_mut().raw_entry_mut().from_hash(hash_, |k| k == &proxy_).insert(proxy_, SharedValue::new(())); )* + $( + + let proxy_ = SymbolProxy($alias.repr); + let hash_ = hash_thing_(dashmap_.hasher(), &proxy_); + let shard_idx_ = dashmap_.determine_shard(hash_ as usize); + dashmap_.shards_mut()[shard_idx_].get_mut().raw_entry_mut().from_hash(hash_, |k| k == &proxy_).insert(proxy_, SharedValue::new(())); + )* } dashmap_ } }; } define_symbols! { + @WITH_NAME: + + self_ = "self", + Self_ = "Self", + tick_static = "'static", + dollar_crate = "$crate", + MISSING_NAME = "[missing name]", + INTEGER_0 = "0", + INTEGER_1 = "1", + INTEGER_2 = "2", + INTEGER_3 = "3", + INTEGER_4 = "4", + INTEGER_5 = "5", + INTEGER_6 = "6", + INTEGER_7 = "7", + INTEGER_8 = "8", + INTEGER_9 = "9", + INTEGER_10 = "10", + INTEGER_11 = "11", + INTEGER_12 = "12", + INTEGER_13 = "13", + INTEGER_14 = "14", + INTEGER_15 = "15", + fn_ = "fn", + + @PLAIN: add_assign, add, alloc, @@ -52,10 +88,88 @@ define_symbols! { bitor, bitxor_assign, bitxor, + transmute_opts, + transmute_trait, + coerce_unsized, + dispatch_from_dyn,destruct, bool, + panic, + begin_panic, + panic_nounwind, + panic_fmt, + panic_misaligned_pointer_dereference, + panic_display, + const_panic_fmt, + panic_bounds_check, + panic_info, + panic_location, + panic_impl, + panic_cannot_unwind, + sized, + unsize, + format_alignment, + start, + format_argument, + format_arguments, + format_count, + format_placeholder, + format_unsafe_arg, + exchange_malloc, + box_free, + drop_in_place, + alloc_layout, + eh_personality, + eh_catch_typeinfo, + phantom_data, + manually_drop, + maybe_uninit, + align_offset, + termination, + tuple_trait, + slice_len_fn, + from_residual, + from_output, + from_yeet, + pointer_like, + const_param_ty, + Poll, + Ready, + Pending, + ResumeTy, + get_context, + Context, + Some, + Err, + Continue, + Break, + into_iter, + new_unchecked, + range_inclusive_new, + CStr, + fn_ptr_trait, + freeze, + coroutine_state, + c_void, + coroutine, + unpin, + pin, + fn_ptr_addr, + structural_teq, + fn_once_output, + copy, + clone, + sync, + discriminant_kind, Box, + structural_peq, boxed, branch, + discriminant_type, + pointee_trait, + metadata_type, + dyn_metadata, + deref_target, + receiver, call_mut, call_once, call, @@ -168,6 +282,7 @@ define_symbols! { not, Not, Ok, + opaque, ops, option_env, option, @@ -204,6 +319,9 @@ define_symbols! { rust_2018, rust_2021, rust_2024, + rustc_coherence_is_core, + rustc_macro_transparency, + semitransparent, shl_assign, shl, shr_assign, @@ -220,6 +338,7 @@ define_symbols! { test_case, test, trace_macros, + transparent, Try, u128, u16, From 292b9de2beaa5d9ccb0ef2beac1da2968f9f1b55 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 12 Jul 2024 17:11:12 +0200 Subject: [PATCH 084/806] Fix cloning Symbols not increasing their ref count --- .../rust-analyzer/crates/intern/src/symbol.rs | 90 +++++++++++++------ .../crates/intern/src/symbol/symbols.rs | 1 + 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 1517f516fc15d..9f7a788d006c8 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -5,7 +5,7 @@ use std::{ borrow::Borrow, fmt, hash::{BuildHasherDefault, Hash, Hasher}, - mem, + mem::{self, ManuallyDrop}, ptr::NonNull, sync::OnceLock, }; @@ -25,6 +25,15 @@ const _: () = assert!(std::mem::align_of::>() == std::mem::align_of::<& const _: () = assert!(std::mem::size_of::>>() == std::mem::size_of::<&&str>()); const _: () = assert!(std::mem::align_of::>>() == std::mem::align_of::<&&str>()); +const _: () = + assert!(std::mem::size_of::<*const *const str>() == std::mem::size_of::()); +const _: () = + assert!(std::mem::align_of::<*const *const str>() == std::mem::align_of::()); + +const _: () = assert!(std::mem::size_of::>>() == std::mem::size_of::()); +const _: () = + assert!(std::mem::align_of::>>() == std::mem::align_of::()); + /// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or /// `Arc>` but its size is that of a thin pointer. The active variant is encoded as a tag /// in the LSB of the alignment niche. @@ -40,19 +49,24 @@ impl TaggedArcPtr { const BOOL_BITS: usize = true as usize; const fn non_arc(r: &'static &'static str) -> Self { - Self { - // SAFETY: The pointer is non-null as it is derived from a reference - // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the - // packing stuff requires reading out the pointer to an integer which is not supported - // in const contexts, so here we make use of the fact that for the non-arc version the - // tag is false (0) and thus does not need touching the actual pointer value.ext) - packed: unsafe { - NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut()) - }, - } + assert!( + mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS + ); + // SAFETY: The pointer is non-null as it is derived from a reference + // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the + // packing stuff requires reading out the pointer to an integer which is not supported + // in const contexts, so here we make use of the fact that for the non-arc version the + // tag is false (0) and thus does not need touching the actual pointer value.ext) + + let packed = + unsafe { NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut()) }; + Self { packed } } fn arc(arc: Arc>) -> Self { + assert!( + mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS + ); Self { packed: Self::pack_arc( // Safety: `Arc::into_raw` always returns a non null pointer @@ -63,12 +77,14 @@ impl TaggedArcPtr { /// Retrieves the tag. #[inline] - pub(crate) fn try_as_arc_owned(self) -> Option>> { + pub(crate) fn try_as_arc_owned(self) -> Option>>> { // Unpack the tag from the alignment niche let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS; if tag != 0 { // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` - Some(unsafe { Arc::from_raw(self.pointer().as_ptr().cast::>()) }) + Some(ManuallyDrop::new(unsafe { + Arc::from_raw(self.pointer().as_ptr().cast::>()) + })) } else { None } @@ -122,10 +138,11 @@ impl TaggedArcPtr { } } -#[derive(PartialEq, Eq, Hash, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Debug)] pub struct Symbol { repr: TaggedArcPtr, } + const _: () = assert!(std::mem::size_of::() == std::mem::size_of::>()); const _: () = assert!(std::mem::align_of::() == std::mem::align_of::>()); @@ -185,19 +202,27 @@ impl Symbol { fn drop_slow(arc: &Arc>) { let (mut shard, hash) = Self::select_shard(arc); - if Arc::count(arc) != 2 { - // Another thread has interned another copy - return; + match Arc::count(arc) { + 0 => unreachable!(), + 1 => unreachable!(), + 2 => (), + _ => { + // Another thread has interned another copy + return; + } } - match shard.raw_entry_mut().from_key_hashed_nocheck::(hash, arc.as_ref()) { - RawEntryMut::Occupied(occ) => occ.remove_entry(), - RawEntryMut::Vacant(_) => unreachable!(), - } - .0 - .0 - .try_as_arc_owned() - .unwrap(); + ManuallyDrop::into_inner( + match shard.raw_entry_mut().from_key_hashed_nocheck::(hash, arc.as_ref()) { + RawEntryMut::Occupied(occ) => occ.remove_entry(), + RawEntryMut::Vacant(_) => unreachable!(), + } + .0 + .0 + .try_as_arc_owned() + .unwrap(), + ); + debug_assert_eq!(Arc::count(&arc), 1); // Shrink the backing storage if the shard is less than 50% occupied. if shard.len() * 2 < shard.capacity() { @@ -219,7 +244,13 @@ impl Drop for Symbol { Self::drop_slow(&arc); } // decrement the ref count - drop(arc); + ManuallyDrop::into_inner(arc); + } +} + +impl Clone for Symbol { + fn clone(&self) -> Self { + Self { repr: increase_arc_refcount(self.repr) } } } @@ -228,8 +259,7 @@ fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr { return repr; }; // increase the ref count - mem::forget(arc.clone()); - mem::forget(arc); + mem::forget(Arc::clone(&arc)); repr } @@ -265,6 +295,7 @@ mod tests { let base_len = MAP.get().unwrap().len(); let hello = Symbol::intern("hello"); let world = Symbol::intern("world"); + let more_worlds = world.clone(); let bang = Symbol::intern("!"); let q = Symbol::intern("?"); assert_eq!(MAP.get().unwrap().len(), base_len + 4); @@ -275,6 +306,7 @@ mod tests { drop(q); assert_eq!(MAP.get().unwrap().len(), base_len + 3); let default = Symbol::intern("default"); + let many_worlds = world.clone(); assert_eq!(MAP.get().unwrap().len(), base_len + 3); assert_eq!( "hello default world!", @@ -285,6 +317,8 @@ mod tests { "hello world!", format!("{} {}{}", hello.as_str(), world.as_str(), bang.as_str()) ); + drop(many_worlds); + drop(more_worlds); drop(hello); drop(world); drop(bang); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 04bf4dff50c3e..af1af0536c2e5 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -10,6 +10,7 @@ use crate::{ symbol::{SymbolProxy, TaggedArcPtr}, Symbol, }; + macro_rules! define_symbols { (@WITH_NAME: $($alias:ident = $value:literal),* $(,)? @PLAIN: $($name:ident),* $(,)?) => { $( From fd16a0efeb3ae37960daeb9d5bd3600a1632c16f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 27 Jun 2024 01:29:49 +0300 Subject: [PATCH 085/806] rustc_middle: derivative -> derive-where --- Cargo.lock | 13 ++++++++++++- compiler/rustc_middle/Cargo.toml | 2 +- compiler/rustc_middle/src/mir/query.rs | 13 ++++--------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eba4eed3686ba..66007b2b6fef0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1057,6 +1057,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-where" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "derive_builder" version = "0.20.0" @@ -4348,7 +4359,7 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags 2.5.0", - "derivative", + "derive-where", "either", "field-offset", "gsgdt", diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 3dc592980fdb6..30f26d563ad3a 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derivative = "2.2.0" +derive-where = "1.2.7" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index cd8e28522ecfb..acf4414c4d6fe 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -2,6 +2,7 @@ use crate::mir; use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt}; +use derive_where::derive_where; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; @@ -224,13 +225,7 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// See also `rustc_const_eval::borrow_check::constraints`. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] -#[derive(derivative::Derivative)] -#[derivative( - PartialOrd, - Ord, - PartialOrd = "feature_allow_slow_enum", - Ord = "feature_allow_slow_enum" -)] +#[derive_where(PartialOrd, Ord)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, @@ -240,7 +235,7 @@ pub enum ConstraintCategory<'tcx> { Cast { /// Whether this is an unsizing cast and if yes, this contains the target type. /// Region variables are erased to ReErased. - #[derivative(PartialOrd = "ignore", Ord = "ignore")] + #[derive_where(skip)] unsize_to: Option>, }, @@ -250,7 +245,7 @@ pub enum ConstraintCategory<'tcx> { ClosureBounds, /// Contains the function type if available. - CallArgument(#[derivative(PartialOrd = "ignore", Ord = "ignore")] Option>), + CallArgument(#[derive_where(skip)] Option>), CopyBound, SizedBound, Assignment, From 35ba700d5e091f7f36e1961792c9582ca384d622 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 27 Jun 2024 01:39:48 +0300 Subject: [PATCH 086/806] rustc_span: derivative -> derive-where --- Cargo.lock | 2 +- compiler/rustc_span/Cargo.toml | 2 +- compiler/rustc_span/src/lib.rs | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66007b2b6fef0..43010908fb01e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4738,7 +4738,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "derivative", + "derive-where", "indexmap", "itoa", "md-5", diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 83a554fe31de0..3fdfe77ead972 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -derivative = "2.2.0" +derive-where = "1.2.7" indexmap = { version = "2.0.0" } itoa = "1.0" md5 = { package = "md-5", version = "0.10.0" } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 266956d63d710..ba3ddab984291 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -38,6 +38,7 @@ // this crate without this line making `rustc_span` available. extern crate self as rustc_span; +use derive_where::derive_where; use rustc_data_structures::{outline, AtomicRef}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; @@ -467,18 +468,18 @@ impl FileName { /// `SpanData` is public because `Span` uses a thread-local interner and can't be /// sent to other threads, but some pieces of performance infra run in a separate thread. /// Using `Span` is generally preferred. -#[derive(Clone, Copy, Hash, PartialEq, Eq, derivative::Derivative)] -#[derivative(PartialOrd, Ord)] +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +#[derive_where(PartialOrd, Ord)] pub struct SpanData { pub lo: BytePos, pub hi: BytePos, /// Information about where the macro came from, if this piece of /// code was created by a macro expansion. - #[derivative(PartialOrd = "ignore", Ord = "ignore")] + #[derive_where(skip)] // `SyntaxContext` does not implement `Ord`. // The other fields are enough to determine in-file order. pub ctxt: SyntaxContext, - #[derivative(PartialOrd = "ignore", Ord = "ignore")] + #[derive_where(skip)] // `LocalDefId` does not implement `Ord`. // The other fields are enough to determine in-file order. pub parent: Option, From 70a11c7ba9d486fdf429dc50770db2dbc37b9eed Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 27 Jun 2024 01:59:30 +0300 Subject: [PATCH 087/806] rustc_next_trait_solver: derivative -> derive-where --- Cargo.lock | 2 +- compiler/rustc_next_trait_solver/Cargo.toml | 2 +- .../rustc_next_trait_solver/src/coherence.rs | 7 +++--- .../src/solve/assembly/mod.rs | 4 ++-- .../src/solve/assembly/structural_traits.rs | 4 ++-- .../src/solve/eval_ctxt/mod.rs | 4 ++-- .../src/solve/inspect/build.rs | 22 +++++++------------ 7 files changed, 19 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43010908fb01e..e5b19ed5b557e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4488,7 +4488,7 @@ name = "rustc_next_trait_solver" version = "0.0.0" dependencies = [ "bitflags 2.5.0", - "derivative", + "derive-where", "rustc_ast_ir", "rustc_data_structures", "rustc_index", diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 07cd4ae68d9a2..cb0730e867391 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derivative = "2.2.0" +derive-where = "1.2.7" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index 55f602d907bbd..f22ea41c51222 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; +use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; @@ -108,15 +109,13 @@ impl From for IsFirstInputType { } } -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = "T: Debug"))] +#[derive_where(Debug; I: Interner, T: Debug)] pub enum OrphanCheckErr { NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>), UncoveredTyParams(UncoveredTyParams), } -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = "T: Debug"))] +#[derive_where(Debug; I: Interner, T: Debug)] pub struct UncoveredTyParams { pub uncovered: T, pub local_ty: Option, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 01dde9ca587ca..f74597fcb3914 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use derive_where::derive_where; use rustc_type_ir::elaborate; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; @@ -21,8 +22,7 @@ use crate::solve::{ /// /// It consists of both the `source`, which describes how that goal would be proven, /// and the `result` when using the given `source`. -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""), Clone(bound = ""))] +#[derive_where(Clone, Debug; I: Interner)] pub(super) struct Candidate { pub(super) source: CandidateSource, pub(super) result: CanonicalResponse, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 7df14e81ab5d4..770ac9a929eae 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -1,6 +1,7 @@ //! Code which is used by built-in goals that match "structurally", such a auto //! traits, `Copy`/`Clone`. +use derive_where::derive_where; use rustc_ast_ir::{Movability, Mutability}; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -384,8 +385,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable { pub tupled_inputs_ty: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c23bc8f09ad16..e328284c0010f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir::data_structures::ensure_sufficient_stack; @@ -87,8 +88,7 @@ where pub(super) inspect: ProofTreeBuilder, } -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Debug(bound = ""), Default(bound = ""))] +#[derive_where(Clone, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] // FIXME: This can be made crate-private once `EvalCtxt` also lives in this crate. diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 3e266ddac71fd..36e13cc97d641 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -7,6 +7,7 @@ use std::marker::PhantomData; use std::mem; +use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, search_graph, Interner}; @@ -51,8 +52,7 @@ where /// in the code, only one or two variants are actually possible. /// /// We simply ICE in case that assumption is broken. -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""))] +#[derive_where(Debug; I: Interner)] enum DebugSolver { Root, GoalEvaluation(WipGoalEvaluation), @@ -78,8 +78,7 @@ impl From> for DebugSolver { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipGoalEvaluation { pub uncanonicalized_goal: Goal, pub orig_values: Vec, @@ -96,8 +95,7 @@ impl WipGoalEvaluation { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""))] +#[derive_where(PartialEq, Eq; I: Interner)] pub(in crate::solve) enum WipCanonicalGoalEvaluationKind { Overflow, CycleInStack, @@ -118,8 +116,7 @@ impl std::fmt::Debug for WipCanonicalGoalEvaluationKind { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipCanonicalGoalEvaluation { goal: CanonicalInput, kind: Option>, @@ -153,8 +150,7 @@ impl WipCanonicalGoalEvaluation { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipCanonicalGoalEvaluationStep { /// Unlike `EvalCtxt::var_values`, we append a new /// generic arg here whenever we create a new inference @@ -193,8 +189,7 @@ impl WipCanonicalGoalEvaluationStep { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipProbe { initial_num_var_values: usize, steps: Vec>, @@ -212,8 +207,7 @@ impl WipProbe { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] enum WipProbeStep { AddGoal(GoalSource, inspect::CanonicalState>), NestedProbe(WipProbe), From 168096f663c3268e462c7b9cbe4dd243eb1ded37 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 27 Jun 2024 14:55:44 +0300 Subject: [PATCH 088/806] rustc_type_ir: derivative -> derive-where --- Cargo.lock | 13 +- compiler/rustc_type_ir/Cargo.toml | 2 +- compiler/rustc_type_ir/src/binder.rs | 37 +++--- compiler/rustc_type_ir/src/canonical.rs | 39 ++---- compiler/rustc_type_ir/src/const_kind.rs | 13 +- compiler/rustc_type_ir/src/error.rs | 10 +- compiler/rustc_type_ir/src/generic_arg.rs | 19 +-- compiler/rustc_type_ir/src/opaque_ty.rs | 11 +- compiler/rustc_type_ir/src/outlives.rs | 4 +- compiler/rustc_type_ir/src/predicate.rs | 112 +++--------------- compiler/rustc_type_ir/src/predicate_kind.rs | 13 +- compiler/rustc_type_ir/src/region_kind.rs | 4 +- compiler/rustc_type_ir/src/relate.rs | 13 +- .../src/search_graph/global_cache.rs | 15 +-- .../rustc_type_ir/src/search_graph/mod.rs | 7 +- compiler/rustc_type_ir/src/solve/inspect.rs | 44 +++---- compiler/rustc_type_ir/src/solve/mod.rs | 84 +++---------- compiler/rustc_type_ir/src/ty_kind.rs | 36 ++---- compiler/rustc_type_ir/src/ty_kind/closure.rs | 51 +------- tests/ui/attributes/dump-preds.stderr | 2 +- .../occurs-check/associated-type.next.stderr | 8 +- .../occurs-check/associated-type.old.stderr | 16 +-- .../structually-relate-aliases.stderr | 4 +- .../issue-118950-root-region.stderr | 8 +- 24 files changed, 140 insertions(+), 425 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5b19ed5b557e..3f3daa6e103b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,17 +1046,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive-where" version = "1.2.7" @@ -4878,7 +4867,7 @@ name = "rustc_type_ir" version = "0.0.0" dependencies = [ "bitflags 2.5.0", - "derivative", + "derive-where", "indexmap", "rustc_ast_ir", "rustc_data_structures", diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index e4bf6069caf3d..e20a9e27bbe56 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derivative = "2.2.0" +derive-where = "1.2.7" indexmap = "2.0.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 2531219baecc8..20600fce4c39e 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -3,6 +3,7 @@ use std::hash::Hash; use std::marker::PhantomData; use std::ops::{ControlFlow, Deref}; +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; #[cfg(feature = "nightly")] @@ -25,15 +26,12 @@ use crate::{self as ty, Interner}; /// e.g., `liberate_late_bound_regions`). /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "T: Clone"), - Copy(bound = "T: Copy"), - Hash(bound = "T: Hash"), - PartialEq(bound = "T: PartialEq"), - Eq(bound = "T: Eq"), - Debug(bound = "T: Debug") -)] +#[derive_where(Clone; I: Interner, T: Clone)] +#[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(Hash; I: Interner, T: Hash)] +#[derive_where(PartialEq; I: Interner, T: PartialEq)] +#[derive_where(Eq; I: Interner, T: Eq)] +#[derive_where(Debug; I: Interner, T: Debug)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Binder { value: T, @@ -351,21 +349,18 @@ impl TypeVisitor for ValidateBoundVars { /// /// If you don't have anything to `instantiate`, you may be looking for /// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder). -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "T: Clone"), - Copy(bound = "T: Copy"), - PartialEq(bound = "T: PartialEq"), - Eq(bound = "T: Eq"), - Ord(bound = "T: Ord"), - PartialOrd(bound = "T: Ord"), - Hash(bound = "T: Hash"), - Debug(bound = "T: Debug") -)] +#[derive_where(Clone; I: Interner, T: Clone)] +#[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(PartialEq; I: Interner, T: PartialEq)] +#[derive_where(Eq; I: Interner, T: Eq)] +#[derive_where(Ord; I: Interner, T: Ord)] +#[derive_where(PartialOrd; I: Interner, T: Ord)] +#[derive_where(Hash; I: Interner, T: Hash)] +#[derive_where(Debug; I: Interner, T: Debug)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub struct EarlyBinder { value: T, - #[derivative(Debug = "ignore")] + #[derive_where(skip(Debug))] _tcx: PhantomData, } diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 7b114f565f29e..4e46a3a13aa5a 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; @@ -11,15 +12,12 @@ use crate::{self as ty, Interner, UniverseIndex}; /// A "canonicalized" type `V` is one where all free inference /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "V: Clone"), - Hash(bound = "V: Hash"), - PartialEq(bound = "V: PartialEq"), - Eq(bound = "V: Eq"), - Debug(bound = "V: fmt::Debug"), - Copy(bound = "V: Copy") -)] +#[derive_where(Clone; I: Interner, V: Clone)] +#[derive_where(Hash; I: Interner, V: Hash)] +#[derive_where(PartialEq; I: Interner, V: PartialEq)] +#[derive_where(Eq; I: Interner, V: Eq)] +#[derive_where(Debug; I: Interner, V: fmt::Debug)] +#[derive_where(Copy; I: Interner, V: Copy)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub struct Canonical { @@ -84,15 +82,7 @@ impl fmt::Display for Canonical { /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, /// with fresh inference variables replacing the canonical values. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - Debug(bound = ""), - Eq(bound = ""), - PartialEq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct CanonicalVarInfo { @@ -149,8 +139,7 @@ impl CanonicalVarInfo { /// Describes the "kind" of the canonical variable. This is a "kind" /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Debug(bound = ""))] +#[derive_where(Clone, Copy, Hash, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub enum CanonicalVarKind { @@ -266,15 +255,7 @@ pub enum CanonicalTyVarKind { /// vectors with the original values that were replaced by canonical /// variables. You will need to supply it later to instantiate the /// canonicalized query response. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Hash(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CanonicalVarValues { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f1683f5449f5c..dab2d3bf695cf 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] @@ -10,8 +11,7 @@ use crate::{self as ty, DebruijnIndex, Interner}; use self::ConstKind::*; /// Represents a constant in Rust. -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))] +#[derive_where(Clone, Copy, Hash, Eq; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum ConstKind { /// A const generic parameter. @@ -79,14 +79,7 @@ impl fmt::Debug for ConstKind { } /// An unevaluated (potentially generic) constant used in the type-system. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct UnevaluatedConst { diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 8b59e9a6f481c..8a6d37b7d23f6 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::solve::NoSolution; @@ -21,14 +22,7 @@ impl ExpectedFound { } // Data structures used in type unification -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic)] #[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum TypeError { diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index b158f0f5eee9f..008268c3bffad 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -1,16 +1,10 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use crate::Interner; -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Debug(bound = ""), - Eq(bound = ""), - PartialEq(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub enum GenericArgKind { Lifetime(I::Region), @@ -18,14 +12,7 @@ pub enum GenericArgKind { Const(I::Const), } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Debug(bound = ""), - Eq(bound = ""), - PartialEq(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub enum TermKind { Ty(I::Ty), diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index d8ed4770e2dc1..8fcb9e508a2be 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; @@ -5,15 +6,7 @@ use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; use crate::{self as ty, Interner}; -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = ""), - Copy(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub struct OpaqueTypeKey { diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 10b6f3355d92a..1dd37377085c2 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -2,6 +2,7 @@ //! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that //! RFC for reference. +use derive_where::derive_where; use smallvec::{smallvec, SmallVec}; use crate::data_structures::SsoHashSet; @@ -9,8 +10,7 @@ use crate::inherent::*; use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor}; use crate::{self as ty, Interner}; -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""))] +#[derive_where(Debug; I: Interner)] pub enum Component { Region(I::Region), Param(I::ParamTy), diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index e5bcbc67f946c..93b6512acc506 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -1,6 +1,7 @@ use std::fmt; use std::hash::Hash; +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; @@ -12,15 +13,12 @@ use crate::visit::TypeVisitableExt as _; use crate::{self as ty, Interner}; /// `A: 'region` -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "A: Clone"), - Copy(bound = "A: Copy"), - Hash(bound = "A: Hash"), - PartialEq(bound = "A: PartialEq"), - Eq(bound = "A: Eq"), - Debug(bound = "A: fmt::Debug") -)] +#[derive_where(Clone; I: Interner, A: Clone)] +#[derive_where(Copy; I: Interner, A: Copy)] +#[derive_where(Hash; I: Interner, A: Hash)] +#[derive_where(PartialEq; I: Interner, A: PartialEq)] +#[derive_where(Eq; I: Interner, A: Eq)] +#[derive_where(Debug; I: Interner, A: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct OutlivesPredicate(pub A, pub I::Region); @@ -50,14 +48,7 @@ where /// /// Trait references also appear in object types like `Foo`, but in /// that case the `Self` parameter is absent from the generic parameters. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct TraitRef { @@ -122,14 +113,7 @@ impl ty::Binder> { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct TraitPredicate { @@ -243,15 +227,7 @@ impl fmt::Display for PredicatePolarity { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub enum ExistentialPredicate { @@ -298,14 +274,7 @@ impl ty::Binder> { /// ``` /// The generic parameters don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct ExistentialTraitRef { @@ -351,14 +320,7 @@ impl ty::Binder> { } /// A `ProjectionPredicate` for an `ExistentialTraitRef`. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct ExistentialProjection { @@ -454,15 +416,7 @@ impl AliasTermKind { /// * For a projection, this would be `>::N<...>`. /// * For an inherent projection, this would be `Ty::N<...>`. /// * For an opaque type, there is no explicit syntax. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct AliasTerm { @@ -491,7 +445,7 @@ pub struct AliasTerm { pub def_id: I::DefId, /// This field exists to prevent the creation of `AliasTerm` without using [`AliasTerm::new_from_args`]. - #[derivative(Debug = "ignore")] + #[derive_where(skip(Debug))] _use_alias_term_new_instead: (), } @@ -633,14 +587,7 @@ impl From> for AliasTerm { /// equality between arbitrary types. Processing an instance of /// Form #2 eventually yields one of these `ProjectionPredicate` /// instances to normalize the LHS. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct ProjectionPredicate { @@ -708,14 +655,7 @@ impl fmt::Debug for ProjectionPredicate { /// Used by the new solver. Unlike a `ProjectionPredicate` this can only be /// proven by actually normalizing `alias`. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct NormalizesTo { @@ -750,15 +690,7 @@ impl fmt::Debug for NormalizesTo { /// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates /// whether the `a` type is the type that we should label as "expected" when /// presenting user diagnostics. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct SubtypePredicate { @@ -768,15 +700,7 @@ pub struct SubtypePredicate { } /// Encodes that we have to coerce *from* the `a` type to the `b` type. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct CoercePredicate { diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index b1d0f8d19b396..5b8b22244ba68 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; @@ -7,8 +8,7 @@ use crate::{self as ty, Interner}; /// A clause is something that can appear in where bounds or be inferred /// by implied bounds. -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))] +#[derive_where(Clone, Copy, Hash, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum ClauseKind { @@ -53,14 +53,7 @@ impl PartialEq for ClauseKind { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum PredicateKind { diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 7abcc370c886c..d56700f52eb8c 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] @@ -124,8 +125,7 @@ rustc_index::newtype_index! { /// [1]: https://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ /// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))] +#[derive_where(Clone, Copy, Hash, Eq; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] pub enum RegionKind { /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 0439e7f857fe7..745ee9d54d91d 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -1,5 +1,6 @@ use std::iter; +use derive_where::derive_where; use rustc_ast_ir::Mutability; use tracing::{debug, instrument}; @@ -17,19 +18,11 @@ pub type RelateResult = Result>; /// a miscompilation or unsoundness. /// /// When in doubt, use `VarianceDiagInfo::default()` -#[derive(derivative::Derivative)] -#[derivative( - Copy(bound = ""), - Clone(bound = ""), - Debug(bound = ""), - Default(bound = ""), - PartialEq(bound = ""), - Eq(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Default; I: Interner)] pub enum VarianceDiagInfo { /// No additional information - this is the default. /// We will not add any additional information to error messages. - #[derivative(Default)] + #[derive_where(default)] None, /// We switched our variance because a generic argument occurs inside /// the invariant generic argument of another type. diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 5ccda931f9c5f..be4f1069cd167 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -1,10 +1,10 @@ +use derive_where::derive_where; use rustc_index::IndexVec; use super::{AvailableDepth, Cx, StackDepth, StackEntry}; use crate::data_structures::{HashMap, HashSet}; -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""), Clone(bound = ""), Copy(bound = ""))] +#[derive_where(Debug, Clone, Copy; X: Cx)] struct QueryData { result: X::Result, proof_tree: X::ProofTree, @@ -20,8 +20,7 @@ struct Success { /// This contains results whose computation never hit the /// recursion limit in `success`, and all results which hit /// the recursion limit in `with_overflow`. -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] +#[derive_where(Default; X: Cx)] struct CacheEntry { success: Option>, /// We have to be careful when caching roots of cycles. @@ -32,8 +31,7 @@ struct CacheEntry { with_overflow: HashMap>>, } -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""))] +#[derive_where(Debug; X: Cx)] pub(super) struct CacheData<'a, X: Cx> { pub(super) result: X::Result, pub(super) proof_tree: X::ProofTree, @@ -41,11 +39,10 @@ pub(super) struct CacheData<'a, X: Cx> { pub(super) encountered_overflow: bool, // FIXME: This is currently unused, but impacts the design // by requiring a closure for `Cx::with_global_cache`. + #[allow(dead_code)] pub(super) nested_goals: &'a HashSet, } - -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] +#[derive_where(Default; X: Cx)] pub struct GlobalCache { map: HashMap>, } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index c2204becdfd71..4abf99b1ded8a 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -3,6 +3,7 @@ use std::hash::Hash; use std::marker::PhantomData; use std::mem; +use derive_where::derive_where; use rustc_index::{Idx, IndexVec}; use tracing::debug; @@ -153,8 +154,7 @@ rustc_index::newtype_index! { pub struct StackDepth {} } -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""))] +#[derive_where(Debug; X: Cx)] struct StackEntry { input: X::Input, @@ -226,8 +226,7 @@ struct DetachedEntry { /// /// The provisional cache can theoretically result in changes to the observable behavior, /// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] +#[derive_where(Default; X: Cx)] struct ProvisionalCacheEntry { stack_depth: Option, with_inductive_stack: Option>, diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 0733c730064b0..e25df7a0f6045 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -21,6 +21,7 @@ use crate::solve::{ CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, }; use crate::{Canonical, CanonicalVarValues, Interner}; +use derive_where::derive_where; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt::Debug; use std::hash::Hash; @@ -31,15 +32,12 @@ use std::hash::Hash; /// This is only ever used as [CanonicalState]. Any type information in proof /// trees used mechanically has to be canonicalized as we otherwise leak /// inference variables from a nested `InferCtxt`. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "T: Clone"), - Copy(bound = "T: Copy"), - PartialEq(bound = "T: PartialEq"), - Eq(bound = "T: Eq"), - Hash(bound = "T: Hash"), - Debug(bound = "T: Debug") -)] +#[derive_where(Clone; I: Interner, T: Clone)] +#[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(PartialEq; I: Interner, T: PartialEq)] +#[derive_where(Eq; I: Interner, T: Eq)] +#[derive_where(Hash; I: Interner, T: Hash)] +#[derive_where(Debug; I: Interner, T: Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub struct State { pub var_values: CanonicalVarValues, @@ -52,24 +50,21 @@ pub type CanonicalState = Canonical>; /// for the `CanonicalVarValues` of the canonicalized goal. /// We use this to map any [CanonicalState] from the local `InferCtxt` /// of the solver query to the `InferCtxt` of the caller. -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""))] +#[derive_where(PartialEq, Eq, Hash; I: Interner)] pub struct GoalEvaluation { pub uncanonicalized_goal: Goal, pub orig_values: Vec, pub evaluation: CanonicalGoalEvaluation, } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub struct CanonicalGoalEvaluation { pub goal: CanonicalInput, pub kind: CanonicalGoalEvaluationKind, pub result: QueryResult, } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum CanonicalGoalEvaluationKind { Overflow, CycleInStack, @@ -77,8 +72,7 @@ pub enum CanonicalGoalEvaluationKind { Evaluation { final_revision: I::CanonicalGoalEvaluationStepRef }, } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub struct CanonicalGoalEvaluationStep { pub instantiated_goal: QueryInput, @@ -89,8 +83,7 @@ pub struct CanonicalGoalEvaluationStep { /// A self-contained computation during trait solving. This either /// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation /// of a goal. -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = ""), PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""))] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub struct Probe { /// What happened inside of this probe in chronological order. pub steps: Vec>, @@ -98,8 +91,7 @@ pub struct Probe { pub final_state: CanonicalState, } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum ProbeStep { /// We added a goal to the `EvalCtxt` which will get proven /// the next time `EvalCtxt::try_evaluate_added_goals` is called. @@ -121,15 +113,7 @@ pub enum ProbeStep { /// What kind of probe we're in. In case the probe represents a candidate, or /// the final result of the current goal - via [ProbeKind::Root] - we also /// store the [QueryResult]. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Hash(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub enum ProbeKind { /// The root inference context while proving a goal. diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 7934f996f0bd2..7ac13755f5ffe 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -3,6 +3,7 @@ pub mod inspect; use std::fmt; use std::hash::Hash; +use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; @@ -89,15 +90,12 @@ pub struct NoSolution; /// /// Most of the time the `param_env` contains the `where`-bounds of the function /// we're currently typechecking while the `predicate` is some trait bound. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "P: Clone"), - Copy(bound = "P: Copy"), - Hash(bound = "P: Hash"), - PartialEq(bound = "P: PartialEq"), - Eq(bound = "P: Eq"), - Debug(bound = "P: fmt::Debug") -)] +#[derive_where(Clone; I: Interner, P: Clone)] +#[derive_where(Copy; I: Interner, P: Copy)] +#[derive_where(Hash; I: Interner, P: Hash)] +#[derive_where(PartialEq; I: Interner, P: PartialEq)] +#[derive_where(Eq; I: Interner, P: Eq)] +#[derive_where(Debug; I: Interner, P: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct Goal { @@ -140,15 +138,12 @@ pub enum GoalSource { InstantiateHigherRanked, } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "Goal: Clone"), - Copy(bound = "Goal: Copy"), - Hash(bound = "Goal: Hash"), - PartialEq(bound = "Goal: PartialEq"), - Eq(bound = "Goal: Eq"), - Debug(bound = "Goal: fmt::Debug") -)] +#[derive_where(Clone; I: Interner, Goal: Clone)] +#[derive_where(Copy; I: Interner, Goal: Copy)] +#[derive_where(Hash; I: Interner, Goal: Hash)] +#[derive_where(PartialEq; I: Interner, Goal: PartialEq)] +#[derive_where(Eq; I: Interner, Goal: Eq)] +#[derive_where(Debug; I: Interner, Goal: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct QueryInput { @@ -157,15 +152,7 @@ pub struct QueryInput { } /// Opaques that are defined in the inference context before a query is called. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = ""), - Default(bound = "") -)] +#[derive_where(Clone, Hash, PartialEq, Eq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct PredefinedOpaquesData { @@ -173,15 +160,7 @@ pub struct PredefinedOpaquesData { } /// Possible ways the given goal can be proven. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] pub enum CandidateSource { /// A user written impl. /// @@ -265,15 +244,7 @@ pub enum BuiltinImplSource { TupleUnsizing, } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Response { @@ -284,15 +255,7 @@ pub struct Response { } /// Additional constraints returned on success. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = ""), - Default(bound = "") -)] +#[derive_where(Clone, Hash, PartialEq, Eq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct ExternalConstraintsData { @@ -301,15 +264,7 @@ pub struct ExternalConstraintsData { pub normalization_nested_goals: NestedNormalizationGoals, } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = ""), - Default(bound = "") -)] +#[derive_where(Clone, Hash, PartialEq, Eq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NestedNormalizationGoals(pub Vec<(GoalSource, Goal)>); @@ -386,8 +341,7 @@ impl MaybeCause { } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +#[derive_where(PartialEq, Eq, Debug; I: Interner)] pub struct CacheData { pub result: QueryResult, pub proof_tree: Option, diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 4ffebef9f1f9b..29993a50aeea0 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,3 +1,5 @@ +use derive_where::derive_where; + #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] @@ -64,8 +66,7 @@ impl AliasTyKind { /// Types written by the user start out as `hir::TyKind` and get /// converted to this representation using `::lower_ty`. #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))] +#[derive_where(Clone, Copy, Hash, Eq; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum TyKind { /// The primitive boolean type. Written as `bool`. @@ -416,15 +417,7 @@ impl fmt::Debug for TyKind { /// * For a projection, this would be `>::N<...>`. /// * For an inherent projection, this would be `Ty::N<...>`. /// * For an opaque type, there is no explicit syntax. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] pub struct AliasTy { @@ -453,7 +446,7 @@ pub struct AliasTy { pub def_id: I::DefId, /// This field exists to prevent the creation of `AliasTy` without using [`AliasTy::new_from_args`]. - #[derivative(Debug = "ignore")] + #[derive_where(skip(Debug))] pub(crate) _use_alias_ty_new_instead: (), } @@ -944,15 +937,7 @@ impl fmt::Debug for InferTy { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Hash(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub struct TypeAndMut { @@ -960,14 +945,7 @@ pub struct TypeAndMut { pub mutbl: Mutability, } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Hash(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct FnSig { diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 24a7c0c67e90f..36f02704de959 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use derive_where::derive_where; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::fold::{shift_region, TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -100,15 +101,7 @@ use crate::{self as ty, Interner}; /// * `GR`: The "return type", which is the type of value returned upon /// completion of the coroutine. /// * `GW`: The "coroutine witness". -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct ClosureArgs { /// Lifetime and type parameters from the enclosing function, @@ -210,15 +203,7 @@ impl ClosureArgs { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineClosureArgs { pub args: I::GenericArgs, @@ -370,15 +355,7 @@ impl TypeVisitor for HasRegionsBoundAt { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub struct CoroutineClosureSignature { pub interior: I::Ty, @@ -552,15 +529,7 @@ impl TypeFolder for FoldEscapingRegions { } } -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub struct GenSig { pub resume_ty: I::Ty, @@ -569,15 +538,7 @@ pub struct GenSig { } /// Similar to `ClosureArgs`; see the above documentation for more. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Hash(bound = ""), - PartialEq(bound = ""), - Eq(bound = ""), - Debug(bound = "") -)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineArgs { pub args: I::GenericArgs, diff --git a/tests/ui/attributes/dump-preds.stderr b/tests/ui/attributes/dump-preds.stderr index 26834376e7612..bdfcbed71e9a6 100644 --- a/tests/ui/attributes/dump-preds.stderr +++ b/tests/ui/attributes/dump-preds.stderr @@ -31,7 +31,7 @@ error: rustc_dump_item_bounds LL | type Assoc: std::ops::Deref | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: Binder { value: ProjectionPredicate(AliasTerm { args: [Alias(Projection, AliasTy { args: [Self/#0, T/#1, P/#2], def_id: DefId(..) })], def_id: DefId(..) }, Term::Ty(())), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(AliasTerm { args: [Alias(Projection, AliasTy { args: [Self/#0, T/#1, P/#2], def_id: DefId(..), .. })], def_id: DefId(..), .. }, Term::Ty(())), bound_vars: [] } = note: Binder { value: TraitPredicate(<>::Assoc

as std::ops::Deref>, polarity:Positive), bound_vars: [] } = note: Binder { value: TraitPredicate(<>::Assoc

as std::marker::Sized>, polarity:Positive), bound_vars: [] } diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index 4f15be4c7c883..9544bdbb468de 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,7 +1,7 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index 329086ab7dfdf..ccc7f30fa6fdb 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,11 +1,11 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr index 2f1dfd19c483b..7de30efae1cd2 100644 --- a/tests/ui/higher-ranked/structually-relate-aliases.stderr +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -1,5 +1,5 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. } error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied --> $DIR/structually-relate-aliases.rs:13:36 | diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 17da1f5247963..d14b9d217da23 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -25,10 +25,10 @@ help: this trait has no implementations, consider adding one LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } error[E0119]: conflicting implementations of trait `Overlap` for type `fn(_)` --> $DIR/issue-118950-root-region.rs:19:1 | From 17c4e4a923d8a5db4a67bd42e18f8116eee78dfd Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 27 Jun 2024 14:56:27 +0300 Subject: [PATCH 089/806] derivative -> derive-where --- src/tools/tidy/src/deps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3c72fae0881e9..0e51441bf3f58 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -254,7 +254,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "darling_macro", "datafrog", "deranged", - "derivative", + "derive-where", "derive_more", "derive_setters", "digest", From 59f88d3db92cddc209b15a4edf26216f5ec9daaa Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 10 Jul 2024 01:25:57 +0300 Subject: [PATCH 090/806] Silence clippy::derived_hash_with_manual_eq for now --- compiler/rustc_type_ir/src/canonical.rs | 3 +++ compiler/rustc_type_ir/src/const_kind.rs | 3 +++ compiler/rustc_type_ir/src/predicate_kind.rs | 3 +++ compiler/rustc_type_ir/src/region_kind.rs | 3 +++ compiler/rustc_type_ir/src/ty_kind.rs | 3 +++ 5 files changed, 15 insertions(+) diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 4e46a3a13aa5a..f02ada152a440 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; @@ -167,6 +169,7 @@ pub enum CanonicalVarKind { PlaceholderConst(I::PlaceholderConst), } +// FIXME(GrigorenkoPV): consider not implementing PartialEq manually impl PartialEq for CanonicalVarKind { fn eq(&self, other: &Self) -> bool { match (self, other) { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index dab2d3bf695cf..685bb5f9a8bf5 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -43,6 +45,7 @@ pub enum ConstKind { Expr(I::ExprConst), } +// FIXME(GrigorenkoPV): consider not implementing PartialEq manually impl PartialEq for ConstKind { fn eq(&self, other: &Self) -> bool { match (self, other) { diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 5b8b22244ba68..70b7c29bdfcc1 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; @@ -38,6 +40,7 @@ pub enum ClauseKind { ConstEvaluatable(I::Const), } +// FIXME(GrigorenkoPV): consider not implementing PartialEq manually impl PartialEq for ClauseKind { fn eq(&self, other: &Self) -> bool { match (self, other) { diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index d56700f52eb8c..61ac83fcc98c5 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -193,6 +195,7 @@ const fn regionkind_discriminant(value: &RegionKind) -> usize { } } +// FIXME(GrigorenkoPV): consider not implementing PartialEq manually // This is manually implemented because a derive would require `I: PartialEq` impl PartialEq for RegionKind { #[inline] diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 29993a50aeea0..db883511d75a5 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + use derive_where::derive_where; #[cfg(feature = "nightly")] @@ -293,6 +295,7 @@ const fn tykind_discriminant(value: &TyKind) -> usize { } } +// FIXME(GrigorenkoPV): consider not implementing PartialEq manually // This is manually implemented because a derive would require `I: PartialEq` impl PartialEq for TyKind { #[inline] From 5a292c954195678125a20aa219c2d2b3b718c56f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 13 Jul 2024 07:09:17 +0900 Subject: [PATCH 091/806] Allow macro expansions into `RestPat` in tuple args work as ellipsis like plain `RestPat` --- .../crates/hir-def/src/body/lower.rs | 44 ++++++++++-- .../src/handlers/mismatched_arg_count.rs | 69 +++++++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index be7068c807a5f..08490470eb142 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -4,6 +4,7 @@ use std::mem; use base_db::CrateId; +use either::Either; use hir_expand::{ name::{name, AsName, Name}, ExpandError, InFile, @@ -1432,14 +1433,12 @@ impl ExprCollector<'_> { has_leading_comma: bool, binding_list: &mut BindingList, ) -> (Box<[PatId]>, Option) { + let args: Vec<_> = args.map(|p| self.collect_pat_possibly_rest(p, binding_list)).collect(); // Find the location of the `..`, if there is one. Note that we do not // consider the possibility of there being multiple `..` here. - let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); + let ellipsis = args.iter().position(|p| p.is_right()); // We want to skip the `..` pattern here, since we account for it above. - let mut args: Vec<_> = args - .filter(|p| !matches!(p, ast::Pat::RestPat(_))) - .map(|p| self.collect_pat(p, binding_list)) - .collect(); + let mut args: Vec<_> = args.into_iter().filter_map(Either::left).collect(); // if there is a leading comma, the user is most likely to type out a leading pattern // so we insert a missing pattern at the beginning for IDE features if has_leading_comma { @@ -1449,6 +1448,41 @@ impl ExprCollector<'_> { (args.into_boxed_slice(), ellipsis) } + // `collect_pat` rejects `ast::Pat::RestPat`, but it should be handled in some cases that + // it is the macro expansion result of an arg sub-pattern in a slice or tuple pattern. + fn collect_pat_possibly_rest( + &mut self, + pat: ast::Pat, + binding_list: &mut BindingList, + ) -> Either { + match &pat { + ast::Pat::RestPat(_) => Either::Right(()), + ast::Pat::MacroPat(mac) => match mac.macro_call() { + Some(call) => { + let macro_ptr = AstPtr::new(&call); + let src = self.expander.in_file(AstPtr::new(&pat)); + let pat = + self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { + if let Some(expanded_pat) = expanded_pat { + this.collect_pat_possibly_rest(expanded_pat, binding_list) + } else { + Either::Left(this.missing_pat()) + } + }); + if let Some(pat) = pat.left() { + self.source_map.pat_map.insert(src, pat); + } + pat + } + None => { + let ptr = AstPtr::new(&pat); + Either::Left(self.alloc_pat(Pat::Missing, ptr)) + } + }, + _ => Either::Left(self.collect_pat(pat, binding_list)), + } + } + // endregion: patterns /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 41c762c85b2cd..4242422663796 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -256,6 +256,75 @@ impl Foo { ); } + #[test] + fn rest_pat_in_macro_expansion() { + check_diagnostics( + r#" +// issue #17292 +#![allow(dead_code)] + +macro_rules! replace_with_2_dots { + ( $( $input:tt )* ) => { + .. + }; +} + +macro_rules! enum_str { + ( + $( + $variant:ident ( + $( $tfield:ty ),* + ) + ) + , + * + ) => { + enum Foo { + $( + $variant ( $( $tfield ),* ), + )* + } + + impl Foo { + fn variant_name_as_str(&self) -> &str { + match self { + $( + Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) ) + => "", + )* + } + } + } + }; +} + +enum_str! { + TupleVariant1(i32), + TupleVariant2(), + TupleVariant3(i8,u8,i128) +} +"#, + ); + + check_diagnostics( + r#" +#![allow(dead_code)] +macro_rules! two_dots1 { + () => { .. }; +} + +macro_rules! two_dots2 { + () => { two_dots1!() }; +} + +fn test() { + let (_, _, two_dots1!()) = ((), 42); + let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3); +} +"#, + ); + } + #[test] fn varargs() { check_diagnostics( From 377a0a7696bbd33c38ac418e8c75428a147eb688 Mon Sep 17 00:00:00 2001 From: bors Date: Thu, 11 Jul 2024 08:55:34 +0000 Subject: [PATCH 092/806] Trigger VSCode to rename after extract variable assist is applied When the user applies the "Extract Variable" assist, the cursor is positioned at the newly inserted variable. This commit adds a command to the assist that triggers the rename action in VSCode. This way, the user can quickly rename the variable after applying the assist. Fixes part of: #17579 --- .../crates/ide-assists/src/assist_context.rs | 6 +++--- .../src/handlers/extract_variable.rs | 3 +++ .../crates/ide-assists/src/tests.rs | 20 +++++++++++-------- .../crates/ide-db/src/assists.rs | 11 +++++++++- .../crates/ide-db/src/source_change.rs | 15 ++++++++++---- .../trait_impl_redundant_assoc_item.rs | 2 +- .../src/handlers/typed_hole.rs | 2 +- .../src/handlers/unresolved_field.rs | 8 ++++---- .../src/handlers/unresolved_method.rs | 4 ++-- .../src/handlers/unused_variables.rs | 2 +- .../crates/ide-diagnostics/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/ssr.rs | 10 +++++----- .../crates/rust-analyzer/src/config.rs | 2 ++ .../crates/rust-analyzer/src/lsp/to_proto.rs | 18 +++++++++++++++-- .../rust-analyzer/editors/code/src/client.ts | 1 + .../editors/code/src/commands.ts | 6 ++++++ .../rust-analyzer/editors/code/src/main.ts | 1 + 17 files changed, 80 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs index 8c7670e0cb71a..c459cdb735af0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs @@ -185,11 +185,11 @@ impl Assists { return None; } - let mut trigger_signature_help = false; + let mut command = None; let source_change = if self.resolve.should_resolve(&id) { let mut builder = SourceChangeBuilder::new(self.file); f(&mut builder); - trigger_signature_help = builder.trigger_signature_help; + command = builder.command.take(); Some(builder.finish()) } else { None @@ -197,7 +197,7 @@ impl Assists { let label = Label::new(label); let group = group.cloned(); - self.buf.push(Assist { id, label, group, target, source_change, trigger_signature_help }); + self.buf.push(Assist { id, label, group, target, source_change, command }); Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 36d3122651207..af21a839727a0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -135,6 +135,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } + edit.rename(); } Anchor::Replace(stmt) => { cov_mark::hit!(test_extract_var_expr_stmt); @@ -149,6 +150,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } + edit.rename(); } Anchor::WrapInBlock(to_wrap) => { let indent_to = to_wrap.indent_level(); @@ -192,6 +194,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } + edit.rename(); // fixup indentation of block block.indent(indent_to); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 2dcfda334b806..29dac4c550002 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -454,7 +454,7 @@ pub fn test_some_range(a: int) -> bool { group: None, target: 59..60, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_variable_assist); @@ -470,7 +470,7 @@ pub fn test_some_range(a: int) -> bool { group: None, target: 59..60, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_function_assist); @@ -500,7 +500,7 @@ pub fn test_some_range(a: int) -> bool { group: None, target: 59..60, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_variable_assist); @@ -516,7 +516,7 @@ pub fn test_some_range(a: int) -> bool { group: None, target: 59..60, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_function_assist); @@ -587,7 +587,9 @@ pub fn test_some_range(a: int) -> bool { is_snippet: true, }, ), - trigger_signature_help: false, + command: Some( + Rename, + ), } "#]] .assert_debug_eq(&extract_into_variable_assist); @@ -603,7 +605,7 @@ pub fn test_some_range(a: int) -> bool { group: None, target: 59..60, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_function_assist); @@ -666,7 +668,9 @@ pub fn test_some_range(a: int) -> bool { is_snippet: true, }, ), - trigger_signature_help: false, + command: Some( + Rename, + ), } "#]] .assert_debug_eq(&extract_into_variable_assist); @@ -715,7 +719,7 @@ pub fn test_some_range(a: int) -> bool { is_snippet: true, }, ), - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&extract_into_function_assist); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs index 7a7328f312d20..0ddbde49abc4f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs @@ -29,7 +29,16 @@ pub struct Assist { /// cumbersome, especially if you want to embed an assist into another data /// structure, such as a diagnostic. pub source_change: Option, - pub trigger_signature_help: bool, + /// The command to execute after the assist is applied. + pub command: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Command { + /// Show the parameter hints popup. + TriggerSignatureHelp, + /// Rename the just inserted item. + Rename, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 7ef7b7ae1d0f8..fba377417e30a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -5,7 +5,7 @@ use std::{collections::hash_map::Entry, iter, mem}; -use crate::SnippetCap; +use crate::{assists::Command, SnippetCap}; use base_db::{AnchoredPathBuf, FileId}; use itertools::Itertools; use nohash_hasher::IntMap; @@ -194,7 +194,7 @@ pub struct SourceChangeBuilder { pub edit: TextEditBuilder, pub file_id: FileId, pub source_change: SourceChange, - pub trigger_signature_help: bool, + pub command: Option, /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. pub mutated_tree: Option, @@ -236,7 +236,7 @@ impl SourceChangeBuilder { edit: TextEdit::builder(), file_id, source_change: SourceChange::default(), - trigger_signature_help: false, + command: None, mutated_tree: None, snippet_builder: None, } @@ -304,8 +304,15 @@ impl SourceChangeBuilder { let file_system_edit = FileSystemEdit::MoveFile { src, dst }; self.source_change.push_file_system_edit(file_system_edit); } + + /// Triggers the parameter hint popup after the assist is applied pub fn trigger_signature_help(&mut self) { - self.trigger_signature_help = true; + self.command = Some(Command::TriggerSignatureHelp); + } + + /// Renames the item at the cursor position after the assist is applied + pub fn rename(&mut self) { + self.command = Some(Command::Rename); } /// Adds a tabstop snippet to place the cursor before `node` diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index a470ce72fc3e0..8c50960684dac 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -98,7 +98,7 @@ fn quickfix_for_redundant_assoc_item( group: None, target: range, source_change: Some(source_change_builder.finish()), - trigger_signature_help: false, + command: None, }]) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index 4f04267adb1ea..3fa38ed9eeeec 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -82,7 +82,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option original_range.file_id, TextEdit::replace(original_range.range, code), )), - trigger_signature_help: false, + command: None, }) .collect(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 41357b59622bf..66733223e2734 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -130,7 +130,7 @@ fn add_variant_to_union( group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), - trigger_signature_help: false, + command: None, }) } @@ -173,7 +173,7 @@ fn add_field_to_struct_fix( group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), - trigger_signature_help: false, + command: None, }) } None => { @@ -204,7 +204,7 @@ fn add_field_to_struct_fix( group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), - trigger_signature_help: false, + command: None, }) } Some(FieldList::TupleFieldList(_tuple)) => { @@ -266,7 +266,7 @@ fn method_fix( file_id, TextEdit::insert(range.end(), "()".to_owned()), )), - trigger_signature_help: false, + command: None, }) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 42211cdbe5d46..46722f43d4fbb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -108,7 +108,7 @@ fn field_fix( (file_id, TextEdit::insert(range.start(), "(".to_owned())), (file_id, TextEdit::insert(range.end(), ")".to_owned())), ])), - trigger_signature_help: false, + command: None, }) } @@ -191,7 +191,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) - file_id, TextEdit::replace(range, assoc_func_call_expr_string), )), - trigger_signature_help: false, + command: None, }) } else { None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index fdd4e862cafcd..f3ce306885733 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -73,7 +73,7 @@ fn fixes( diagnostic_range.file_id, TextEdit::replace(name_range, format!("_{}", var_name.display(db))), )), - trigger_signature_help: false, + command: None, }]) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 6d1226d65c5cf..d30ee1a1b966f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -613,7 +613,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { group: None, target, source_change: None, - trigger_signature_help: false, + command: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs index b49fe391bf258..6133c33c71a15 100644 --- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs +++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs @@ -45,7 +45,7 @@ pub(crate) fn ssr_assists( group: Some(GroupLabel("Apply SSR".into())), target: comment_range, source_change, - trigger_signature_help: false, + command: None, }; ssr_assists.push(assist); @@ -143,7 +143,7 @@ mod tests { is_snippet: false, }, ), - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_file_assist); @@ -196,7 +196,7 @@ mod tests { is_snippet: false, }, ), - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_workspace_assist); @@ -236,7 +236,7 @@ mod tests { ), target: 10..21, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_file_assist); @@ -256,7 +256,7 @@ mod tests { ), target: 10..21, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_workspace_assist); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3594cdda2e962..990ef77642c1b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1135,6 +1135,7 @@ pub struct ClientCommandsConfig { pub show_reference: bool, pub goto_location: bool, pub trigger_parameter_hints: bool, + pub rename: bool, } #[derive(Debug)] @@ -1901,6 +1902,7 @@ impl Config { show_reference: get("rust-analyzer.showReferences"), goto_location: get("rust-analyzer.gotoLocation"), trigger_parameter_hints: get("editor.action.triggerParameterHints"), + rename: get("editor.action.rename"), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index de394d3d11879..b0003fa89e3f2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -13,7 +13,7 @@ use ide::{ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; -use ide_db::{rust_doc::format_docs, FxHasher}; +use ide_db::{assists, rust_doc::format_docs, FxHasher}; use itertools::Itertools; use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; @@ -1336,8 +1336,14 @@ pub(crate) fn code_action( command: None, }; - if assist.trigger_signature_help && snap.config.client_commands().trigger_parameter_hints { + if assist.command == Some(assists::Command::TriggerSignatureHelp) + && snap.config.client_commands().trigger_parameter_hints + { res.command = Some(command::trigger_parameter_hints()); + } else if assist.command == Some(assists::Command::Rename) + && snap.config.client_commands().rename + { + res.command = Some(command::rename()); } match (assist.source_change, resolve_data) { @@ -1715,6 +1721,14 @@ pub(crate) mod command { arguments: None, } } + + pub(crate) fn rename() -> lsp_types::Command { + lsp_types::Command { + title: "rename".into(), + command: "rust-analyzer.rename".into(), + arguments: None, + } + } } pub(crate) fn implementation_title(count: usize) -> String { diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts index 542233e7b9187..1599f4b13aea9 100644 --- a/src/tools/rust-analyzer/editors/code/src/client.ts +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -348,6 +348,7 @@ class ExperimentalFeatures implements lc.StaticFeature { "rust-analyzer.showReferences", "rust-analyzer.gotoLocation", "editor.action.triggerParameterHints", + "editor.action.rename", ], }, ...capabilities.experimental, diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 2b0b300106228..72ca2923dda1a 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -118,6 +118,12 @@ export function triggerParameterHints(_: CtxInit): Cmd { }; } +export function rename(_: CtxInit): Cmd { + return async () => { + await vscode.commands.executeCommand("editor.action.rename"); + }; +} + export function openLogs(ctx: CtxInit): Cmd { return async () => { if (ctx.client.outputChannel) { diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index c96f2ae869e0c..a23f3656e3bff 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -190,6 +190,7 @@ function createCommands(): Record { runSingle: { enabled: commands.runSingle }, showReferences: { enabled: commands.showReferences }, triggerParameterHints: { enabled: commands.triggerParameterHints }, + rename: { enabled: commands.rename }, openLogs: { enabled: commands.openLogs }, revealDependency: { enabled: commands.revealDependency }, }; From 39378cf4f405f3c27dacf1e3e1a509c9d73602dc Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 13 Jul 2024 15:46:52 +0700 Subject: [PATCH 093/806] Add more doc-valid-idents * "AccessKit" is a commonly used accessibility toolkit used in Rust GUIs. * "CoreFoundation", "CoreGraphics", "CoreText" are frameworks on Apple OSes. * "Direct2D", "Direct3D", "DirectWrite" are frameworks on Windows * "PostScript" is a programming language and is mentioned when talking about text and vector graphics. * "OpenAL" is an audio framework / API. * "OpenType" is a font format (TrueType is already mentioned). * "WebRTC", "WebSocket", "WebTransport" are web networking technologies. * "NetBSD" and "OpenBSD" are like the already included FreeBSD. --- book/src/lint_configuration.md | 2 +- clippy_config/src/conf.rs | 13 ++++++---- tests/ui/doc/doc-fixable.fixed | 13 ++++++---- tests/ui/doc/doc-fixable.rs | 13 ++++++---- tests/ui/doc/doc-fixable.stderr | 44 ++++++++++++++++----------------- 5 files changed, 47 insertions(+), 38 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index ad29339a84ad6..a750f3619d0d0 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 7f53aad679330..18e481b7af956 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -18,23 +18,26 @@ use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "AccessKit", + "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", - "DirectX", + "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", - "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", + "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", - "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", - "WebGL", "WebGL2", "WebGPU", + "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", + "OpenType", + "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", - "iOS", "macOS", "FreeBSD", + "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 84673f1f43fd5..edfffe8fcfe36 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -54,21 +54,24 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB -/// DirectX +/// AccessKit +/// CoreFoundation CoreGraphics CoreText +/// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry -/// WebGL WebGL2 WebGPU +/// OpenAL OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry +/// OpenType +/// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD +/// iOS macOS FreeBSD NetBSD OpenBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 4d017a99e0fb6..3c0f6913e3289 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -54,21 +54,24 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB -/// DirectX +/// AccessKit +/// CoreFoundation CoreGraphics CoreText +/// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry -/// WebGL WebGL2 WebGPU +/// OpenAL OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry +/// OpenType +/// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD +/// iOS macOS FreeBSD NetBSD OpenBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index a9263f62d38dc..67c0464149c6e 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:75:5 + --> tests/ui/doc/doc-fixable.rs:78:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:92:5 + --> tests/ui/doc/doc-fixable.rs:95:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:100:8 + --> tests/ui/doc/doc-fixable.rs:103:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:103:7 + --> tests/ui/doc/doc-fixable.rs:106:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:105:22 + --> tests/ui/doc/doc-fixable.rs:108:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:106:5 + --> tests/ui/doc/doc-fixable.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:113:5 + --> tests/ui/doc/doc-fixable.rs:116:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:126:5 + --> tests/ui/doc/doc-fixable.rs:129:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:137:43 + --> tests/ui/doc/doc-fixable.rs:140:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:142:5 + --> tests/ui/doc/doc-fixable.rs:145:5 | LL | And BarQuz too. | ^^^^^^ @@ -243,7 +243,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:143:1 + --> tests/ui/doc/doc-fixable.rs:146:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:148:43 + --> tests/ui/doc/doc-fixable.rs:151:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:153:5 + --> tests/ui/doc/doc-fixable.rs:156:5 | LL | And BarQuz too. | ^^^^^^ @@ -276,7 +276,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:154:1 + --> tests/ui/doc/doc-fixable.rs:157:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:165:5 + --> tests/ui/doc/doc-fixable.rs:168:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:184:22 + --> tests/ui/doc/doc-fixable.rs:187:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:208:34 + --> tests/ui/doc/doc-fixable.rs:211:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | ~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:231:22 + --> tests/ui/doc/doc-fixable.rs:234:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()). | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:231:30 + --> tests/ui/doc/doc-fixable.rs:234:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`). | ~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:5 + --> tests/ui/doc/doc-fixable.rs:237:5 | LL | /// ABes | ^^^^ @@ -353,7 +353,7 @@ LL | /// `ABes` | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:9 + --> tests/ui/doc/doc-fixable.rs:243:9 | LL | /// foo() | ^^^^^ @@ -364,7 +364,7 @@ LL | /// `foo()` | ~~~~~~~ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:244:5 + --> tests/ui/doc/doc-fixable.rs:247:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` From 6351a2064261a8e1ec57d7dc8e1b1a1e6bf4e3fe Mon Sep 17 00:00:00 2001 From: Campbell Date: Sat, 13 Jul 2024 21:54:22 +1200 Subject: [PATCH 094/806] feat: Add incorrect case diagnostics for enum variant fields and all variables --- .../hir-ty/src/diagnostics/decl_check.rs | 93 +++++++++++-- .../src/handlers/incorrect_case.rs | 128 +++++++++++++++--- .../src/handlers/missing_match_arms.rs | 4 +- 3 files changed, 194 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 15ecf9aafcfc9..1e6cb85d3b0ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -17,8 +17,8 @@ use std::fmt; use hir_def::{ data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId, - EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, - TraitId, TypeAliasId, + EnumId, EnumVariantId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, + StructId, TraitId, TypeAliasId, }; use hir_expand::{ name::{AsName, Name}, @@ -353,17 +353,16 @@ impl<'a> DeclValidator<'a> { continue; }; - let is_param = ast::Param::can_cast(parent.kind()); - // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, - // because e.g. match arms are patterns as well. - // In other words, we check that it's a named variable binding. - let is_binding = ast::LetStmt::can_cast(parent.kind()) - || (ast::MatchArm::can_cast(parent.kind()) && ident_pat.at_token().is_some()); - if !(is_param || is_binding) { - // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. + let is_shorthand = ast::RecordPatField::cast(parent.clone()) + .map(|parent| parent.name_ref().is_none()) + .unwrap_or_default(); + if is_shorthand { + // We don't check shorthand field patterns, such as 'field' in `Thing { field }`, + // since the shorthand isn't the declaration. continue; } + let is_param = ast::Param::can_cast(parent.kind()); let ident_type = if is_param { IdentType::Parameter } else { IdentType::Variable }; self.create_incorrect_case_diagnostic_for_ast_node( @@ -489,6 +488,11 @@ impl<'a> DeclValidator<'a> { /// Check incorrect names for enum variants. fn validate_enum_variants(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); + + for (variant_id, _) in data.variants.iter() { + self.validate_enum_variant_fields(*variant_id); + } + let mut enum_variants_replacements = data .variants .iter() @@ -551,6 +555,75 @@ impl<'a> DeclValidator<'a> { } } + /// Check incorrect names for fields of enum variant. + fn validate_enum_variant_fields(&mut self, variant_id: EnumVariantId) { + let variant_data = self.db.enum_variant_data(variant_id); + let VariantData::Record(fields) = variant_data.variant_data.as_ref() else { + return; + }; + let mut variant_field_replacements = fields + .iter() + .filter_map(|(_, field)| { + to_lower_snake_case(&field.name.to_smol_str()).map(|new_name| Replacement { + current_name: field.name.clone(), + suggested_text: new_name, + expected_case: CaseType::LowerSnakeCase, + }) + }) + .peekable(); + + // XXX: only look at sources if we do have incorrect names + if variant_field_replacements.peek().is_none() { + return; + } + + let variant_loc = variant_id.lookup(self.db.upcast()); + let variant_src = variant_loc.source(self.db.upcast()); + + let Some(ast::FieldList::RecordFieldList(variant_fields_list)) = + variant_src.value.field_list() + else { + always!( + variant_field_replacements.peek().is_none(), + "Replacements ({:?}) were generated for an enum variant \ + which had no fields list: {:?}", + variant_field_replacements.collect::>(), + variant_src + ); + return; + }; + let mut variant_variants_iter = variant_fields_list.fields(); + for field_replacement in variant_field_replacements { + // We assume that parameters in replacement are in the same order as in the + // actual params list, but just some of them (ones that named correctly) are skipped. + let field = loop { + if let Some(field) = variant_variants_iter.next() { + let Some(field_name) = field.name() else { + continue; + }; + if field_name.as_name() == field_replacement.current_name { + break field; + } + } else { + never!( + "Replacement ({:?}) was generated for an enum variant field \ + which was not found: {:?}", + field_replacement, + variant_src + ); + return; + } + }; + + self.create_incorrect_case_diagnostic_for_ast_node( + field_replacement, + variant_src.file_id, + &field, + IdentType::Field, + ); + } + } + fn validate_const(&mut self, const_id: ConstId) { let container = const_id.lookup(self.db.upcast()).container; if self.is_trait_impl_container(container) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index a0fad7c850c6b..18a95f0963ddb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -332,6 +332,7 @@ impl someStruct { check_diagnostics( r#" enum Option { Some, None } +use Option::{Some, None}; #[allow(unused)] fn main() { @@ -344,24 +345,6 @@ fn main() { ); } - #[test] - fn non_let_bind() { - check_diagnostics( - r#" -enum Option { Some, None } - -#[allow(unused)] -fn main() { - match Option::None { - SOME_VAR @ None => (), - // ^^^^^^^^ 💡 warn: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` - Some => (), - } -} -"#, - ); - } - #[test] fn allow_attributes_crate_attr() { check_diagnostics( @@ -427,7 +410,12 @@ fn qualify() { #[test] // Issue #8809. fn parenthesized_parameter() { - check_diagnostics(r#"fn f((O): _) { _ = O; }"#) + check_diagnostics( + r#" +fn f((_O): u8) {} + // ^^ 💡 warn: Variable `_O` should have snake_case name, e.g. `_o` +"#, + ) } #[test] @@ -766,4 +754,106 @@ mod Foo; "#, ) } + + #[test] + fn test_field_shorthand() { + check_diagnostics( + r#" +struct Foo { _nonSnake: u8 } + // ^^^^^^^^^ 💡 warn: Field `_nonSnake` should have snake_case name, e.g. `_non_snake` +fn func(Foo { _nonSnake }: Foo) {} +"#, + ); + } + + #[test] + fn test_match() { + check_diagnostics( + r#" +enum Foo { Variant { nonSnake1: u8 } } + // ^^^^^^^^^ 💡 warn: Field `nonSnake1` should have snake_case name, e.g. `non_snake1` +fn func() { + match (Foo::Variant { nonSnake1: 1 }) { + Foo::Variant { nonSnake1: _nonSnake2 } => {}, + // ^^^^^^^^^^ 💡 warn: Variable `_nonSnake2` should have snake_case name, e.g. `_non_snake2` + } +} +"#, + ); + + check_diagnostics( + r#" +struct Foo(u8); + +fn func() { + match Foo(1) { + Foo(_nonSnake) => {}, + // ^^^^^^^^^ 💡 warn: Variable `_nonSnake` should have snake_case name, e.g. `_non_snake` + } +} +"#, + ); + + check_diagnostics( + r#" +fn main() { + match 1 { + _Bad1 @ _Bad2 => {} + // ^^^^^ 💡 warn: Variable `_Bad1` should have snake_case name, e.g. `_bad1` + // ^^^^^ 💡 warn: Variable `_Bad2` should have snake_case name, e.g. `_bad2` + } +} +"#, + ); + check_diagnostics( + r#" +fn main() { + match 1 { _Bad1 => () } + // ^^^^^ 💡 warn: Variable `_Bad1` should have snake_case name, e.g. `_bad1` +} +"#, + ); + + check_diagnostics( + r#" +enum Foo { V1, V2 } +use Foo::V1; + +fn main() { + match V1 { + _Bad1 @ V1 => {}, + // ^^^^^ 💡 warn: Variable `_Bad1` should have snake_case name, e.g. `_bad1` + Foo::V2 => {} + } +} +"#, + ); + } + + #[test] + fn test_for_loop() { + check_diagnostics( + r#" +//- minicore: iterators +fn func() { + for _nonSnake in [] {} + // ^^^^^^^^^ 💡 warn: Variable `_nonSnake` should have snake_case name, e.g. `_non_snake` +} +"#, + ); + + check_fix( + r#" +//- minicore: iterators +fn func() { + for nonSnake$0 in [] { nonSnake; } +} +"#, + r#" +fn func() { + for non_snake in [] { non_snake; } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 6d0119fb57c6b..97296278c3a3a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -608,8 +608,8 @@ fn main() { // `Never` is deliberately not defined so that it's an uninferred type. // We ignore these to avoid triggering bugs in the analysis. match Option::::None { - None => (), - Some(never) => match never {}, + Option::None => (), + Option::Some(never) => match never {}, } match Option::::None { Option::Some(_never) => {}, From cc1bb8f57a167d2e01db4c2175bfea1a8f48e772 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Thu, 11 Jul 2024 15:08:22 +0800 Subject: [PATCH 095/806] make [`or_fun_call`] and [`unwrap_or_default`] recursive. --- clippy_lints/src/methods/or_fun_call.rs | 110 ++++++++++++++---------- tests/ui/or_fun_call.fixed | 35 ++++++++ tests/ui/or_fun_call.rs | 35 ++++++++ tests/ui/or_fun_call.stderr | 50 ++++++++++- 4 files changed, 183 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index baa60454419b1..e4326cb958e5f 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,13 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{ + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, +}; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +18,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -26,7 +31,6 @@ pub(super) fn check<'tcx>( /// `or_insert(T::new())` or `or_insert(T::default())`. /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - #[allow(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: &str, @@ -123,8 +127,8 @@ pub(super) fn check<'tcx>( } /// Checks for `*or(foo())`. - #[allow(clippy::too_many_arguments)] - fn check_general_case<'tcx>( + #[expect(clippy::too_many_arguments)] + fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, @@ -135,7 +139,7 @@ pub(super) fn check<'tcx>( span: Span, // None if lambda is required fun_span: Option, - ) { + ) -> bool { // (path, fn_has_argument, methods, suffix) const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ (sym::BTreeEntry, false, &["or_insert"], "with"), @@ -185,54 +189,68 @@ pub(super) fn check<'tcx>( format!("{name}_{suffix}({sugg})"), app, ); - } - } - - let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { - if let hir::ExprKind::Block( - hir::Block { - stmts: [], - expr: Some(expr), - .. - }, - _, - ) = arg.kind - { - expr + true } else { - arg + false } - }; + } if let [arg] = args { - let inner_arg = extract_inner_arg(arg); - match inner_arg.kind { - hir::ExprKind::Call(fun, or_args) => { - let or_has_args = !or_args.is_empty(); - if or_has_args - || !check_unwrap_or_default(cx, name, receiver, fun, Some(inner_arg), expr.span, method_span) - { - let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); - } - }, - hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) => { - check_unwrap_or_default(cx, name, receiver, inner_arg, None, expr.span, method_span); - }, - hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); - }, - _ => (), - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + // `or_fun_call` lint needs to take nested expr into account, + // but `unwrap_or_default` lint doesn't, we don't want something like: + // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by + // `opt.unwrap_or_default()`. + let is_nested_expr = ex.hir_id != inner_arg.hir_id; + + let is_triggered = match ex.kind { + hir::ExprKind::Call(fun, fun_args) => { + let inner_fun_has_args = !fun_args.is_empty(); + let fun_span = if inner_fun_has_args || is_nested_expr { + None + } else { + Some(fun.span) + }; + (!inner_fun_has_args + && !is_nested_expr + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) + }, + hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + }, + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) + }, + _ => false, + }; + + if is_triggered { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }); } // `map_or` takes two arguments if let [arg, lambda] = args { - let inner_arg = extract_inner_arg(arg); - if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { - let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; - check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + let is_top_most_expr = ex.hir_id == inner_arg.hir_id; + if let hir::ExprKind::Call(fun, fun_args) = ex.kind { + let fun_span = if fun_args.is_empty() && is_top_most_expr { + Some(fun.span) + } else { + None + }; + if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span) { + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + }); } } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 3501559297188..7452eb7768885 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -330,4 +330,39 @@ mod issue_10228 { } } +// issue #12973 +fn fn_call_in_nested_expr() { + struct Foo { + val: String, + } + + fn f() -> i32 { + 1 + } + let opt: Option = Some(1); + + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(f); // suggest `.unwrap_or_else(f)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(|| f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(|| { + let x = f(); + x + 1 + }); + //~v ERROR: use of `map_or` followed by a function call + let _ = opt.map_or_else(|| f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + // + //~v ERROR: use of `unwrap_or` to construct default value + let _ = opt.unwrap_or_default(); + + let opt_foo = Some(Foo { + val: String::from("123"), + }); + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 3dcf657d74350..cd6f7bb207012 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -330,4 +330,39 @@ mod issue_10228 { } } +// issue #12973 +fn fn_call_in_nested_expr() { + struct Foo { + val: String, + } + + fn f() -> i32 { + 1 + } + let opt: Option = Some(1); + + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or({ + let x = f(); + x + 1 + }); + //~v ERROR: use of `map_or` followed by a function call + let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + // + //~v ERROR: use of `unwrap_or` to construct default value + let _ = opt.unwrap_or({ i32::default() }); + + let opt_foo = Some(Foo { + val: String::from("123"), + }); + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt_foo.unwrap_or(Foo { val: String::default() }); +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 3070db22fc5a6..06f804fb41e86 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -196,5 +196,53 @@ error: use of `unwrap_or_else` to construct default value LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: aborting due to 32 previous errors +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:345:17 + | +LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:348:17 + | +LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:351:17 + | +LL | let _ = opt.unwrap_or({ + | _________________^ +LL | | let x = f(); +LL | | x + 1 +LL | | }); + | |______^ + | +help: try + | +LL ~ let _ = opt.unwrap_or_else(|| { +LL + let x = f(); +LL + x + 1 +LL ~ }); + | + +error: use of `map_or` followed by a function call + --> tests/ui/or_fun_call.rs:356:17 + | +LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:359:17 + | +LL | let _ = opt.unwrap_or({ i32::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:365:21 + | +LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` + +error: aborting due to 38 previous errors From fd81880c9108c4b98839d55fe29884a625f05f02 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Fri, 12 Jul 2024 15:58:59 +0200 Subject: [PATCH 096/806] Leave a trace of the current suboptimal status of foreign_write --- src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 9c19ae76a2d01..8e23257b6c006 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -146,6 +146,12 @@ mod transition { /// non-protected interior mutable `Reserved` which stay the same. fn foreign_write(state: PermissionPriv, protected: bool) -> Option { Some(match state { + // FIXME: since the fix related to reservedim_spurious_write, it is now possible + // to express these transitions of the state machine without an explicit dependency + // on `protected`: because `ty_is_freeze: false` implies `!protected` then + // the line handling `Reserved { .. } if protected` could be deleted. + // This will however require optimizations to the exhaustive tests because + // fewer initial conditions are valid. Reserved { .. } if protected => Disabled, res @ Reserved { ty_is_freeze: false, .. } => res, _ => Disabled, From e03998bb639e8e22dd12d870d2fa059f9cb671e0 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sat, 13 Jul 2024 19:33:35 -0700 Subject: [PATCH 097/806] Address feedback from @DropDemBits - move `edit.rename()` to the end of the function - use a match statement to set `res.command` --- .../src/handlers/extract_variable.rs | 4 +--- .../crates/rust-analyzer/src/config.rs | 2 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 17 ++++++++--------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index af21a839727a0..cb34f8d81a33f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -135,7 +135,6 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } - edit.rename(); } Anchor::Replace(stmt) => { cov_mark::hit!(test_extract_var_expr_stmt); @@ -150,7 +149,6 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } - edit.rename(); } Anchor::WrapInBlock(to_wrap) => { let indent_to = to_wrap.indent_level(); @@ -194,12 +192,12 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } } } - edit.rename(); // fixup indentation of block block.indent(indent_to); } } + edit.rename(); }, ) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 990ef77642c1b..91cde4dc0adbb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1128,7 +1128,7 @@ pub struct WorkspaceSymbolConfig { /// How many items are returned at most. pub search_limit: usize, } - +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ClientCommandsConfig { pub run_single: bool, pub debug_single: bool, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index b0003fa89e3f2..323926e435c9f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1336,15 +1336,14 @@ pub(crate) fn code_action( command: None, }; - if assist.command == Some(assists::Command::TriggerSignatureHelp) - && snap.config.client_commands().trigger_parameter_hints - { - res.command = Some(command::trigger_parameter_hints()); - } else if assist.command == Some(assists::Command::Rename) - && snap.config.client_commands().rename - { - res.command = Some(command::rename()); - } + let commands = snap.config.client_commands(); + res.command = match assist.command { + Some(assists::Command::TriggerSignatureHelp) if commands.trigger_parameter_hints => { + Some(command::trigger_parameter_hints()) + } + Some(assists::Command::Rename) if commands.rename => Some(command::rename()), + _ => None, + }; match (assist.source_change, resolve_data) { (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?), From b32fc1fcf6734ccbd0dab2a65b2d8969e7a178b7 Mon Sep 17 00:00:00 2001 From: Anita Hammer <166057949+anitahammer@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:26:41 +0100 Subject: [PATCH 098/806] Update manual.adoc --- src/tools/rust-analyzer/docs/user/manual.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index e1c1c54ec4185..a177720908748 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -623,7 +623,7 @@ https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]! **Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs] -The <<_installation,Installation>> section contains details on configuration for some of the editors. +The <> section contains details on configuration for some of the editors. In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files. Some clients, such as <> or <> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`. From e13eb37eeb3ccb447f33496fe86d77498558d2d5 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sun, 14 Jul 2024 17:46:25 +0530 Subject: [PATCH 099/806] Fix malformed suggestion for repeated maybe unsized bounds --- compiler/rustc_hir/src/hir.rs | 24 ++- compiler/rustc_middle/src/ty/diagnostics.rs | 69 +++++-- ...tionf-for-repeated-unsized-bound-127441.rs | 39 ++++ ...f-for-repeated-unsized-bound-127441.stderr | 192 ++++++++++++++++++ 4 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs create mode 100644 tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d57fad6ba4c2d..0f1b2bec6c648 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -723,7 +723,7 @@ impl<'hir> Generics<'hir> { ) } - fn span_for_predicate_removal(&self, pos: usize) -> Span { + pub fn span_for_predicate_removal(&self, pos: usize) -> Span { let predicate = &self.predicates[pos]; let span = predicate.span(); @@ -766,15 +766,21 @@ impl<'hir> Generics<'hir> { return self.span_for_predicate_removal(predicate_pos); } - let span = bounds[bound_pos].span(); - if bound_pos == 0 { - // where T: ?Sized + Bar, Foo: Bar, - // ^^^^^^^^^ - span.to(bounds[1].span().shrink_to_lo()) + let bound_span = bounds[bound_pos].span(); + if bound_pos < bounds.len() - 1 { + // If there's another bound after the current bound + // include the following '+' e.g.: + // + // `T: Foo + CurrentBound + Bar` + // ^^^^^^^^^^^^^^^ + bound_span.to(bounds[bound_pos + 1].span().shrink_to_lo()) } else { - // where T: Bar + ?Sized, Foo: Bar, - // ^^^^^^^^^ - bounds[bound_pos - 1].span().shrink_to_hi().to(span) + // If the current bound is the last bound + // include the preceding '+' E.g.: + // + // `T: Foo + Bar + CurrentBound` + // ^^^^^^^^^^^^^^^ + bound_span.with_lo(bounds[bound_pos - 1].span().hi()) } } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 4bf2233799130..3a5cb22be388f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -188,31 +188,60 @@ fn suggest_changing_unsized_bound( continue; }; - for (pos, bound) in predicate.bounds.iter().enumerate() { - let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else { - continue; - }; - if poly.trait_ref.trait_def_id() != def_id { - continue; - } - if predicate.origin == PredicateOrigin::ImplTrait && predicate.bounds.len() == 1 { - // For `impl ?Sized` with no other bounds, suggest `impl Sized` instead. - let bound_span = bound.span(); - if bound_span.can_be_used_for_suggestions() { - let question_span = bound_span.with_hi(bound_span.lo() + BytePos(1)); - suggestions.push(( + let unsized_bounds = predicate + .bounds + .iter() + .enumerate() + .filter(|(_, bound)| { + if let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound + && poly.trait_ref.trait_def_id() == def_id + { + true + } else { + false + } + }) + .collect::>(); + + if unsized_bounds.is_empty() { + continue; + } + + let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg)); + + if predicate.bounds.len() == unsized_bounds.len() { + // All the bounds are unsized bounds, e.g. + // `T: ?Sized + ?Sized` or `_: impl ?Sized + ?Sized`, + // so in this case: + // - if it's an impl trait predicate suggest changing the + // the first bound to sized and removing the rest + // - Otherwise simply suggest removing the entire predicate + if predicate.origin == PredicateOrigin::ImplTrait { + let first_bound = unsized_bounds[0].1; + let first_bound_span = first_bound.span(); + if first_bound_span.can_be_used_for_suggestions() { + let question_span = + first_bound_span.with_hi(first_bound_span.lo() + BytePos(1)); + push_suggestion( question_span, - String::new(), SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized, - )); + ); + + for (pos, _) in unsized_bounds.iter().skip(1) { + let sp = generics.span_for_bound_removal(where_pos, *pos); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); + } } } else { + let sp = generics.span_for_predicate_removal(where_pos); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); + } + } else { + // Some of the bounds are other than unsized. + // So push separate removal suggestion for each unsized bound + for (pos, _) in unsized_bounds { let sp = generics.span_for_bound_removal(where_pos, pos); - suggestions.push(( - sp, - String::new(), - SuggestChangingConstraintsMessage::RemoveMaybeUnsized, - )); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); } } } diff --git a/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs new file mode 100644 index 0000000000000..e6d7f74880fb2 --- /dev/null +++ b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs @@ -0,0 +1,39 @@ +// Regression test for #127441 + +// Tests that we make the correct suggestion +// in case there are more than one `?Sized` +// bounds on a function parameter + +use std::fmt::Debug; + +fn foo1(a: T) {} +//~^ ERROR he size for values of type `T` cannot be known at compilation time + +fn foo2(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `T` cannot be known at compilation time + +fn foo3(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR he size for values of type `T` cannot be known at compilation time + +fn foo4(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `T` cannot be known at compilation time + +fn foo5(_: impl ?Sized) {} +//~^ ERROR the size for values of type `impl ?Sized` cannot be known at compilation time + +fn foo6(_: impl ?Sized + ?Sized) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + ?Sized` cannot be known at compilation tim + +fn foo7(_: impl ?Sized + ?Sized + Debug) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + ?Sized + Debug` cannot be known at compilation time + +fn foo8(_: impl ?Sized + Debug + ?Sized ) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + Debug + ?Sized` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr new file mode 100644 index 0000000000000..3e8f45ee9fc26 --- /dev/null +++ b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr @@ -0,0 +1,192 @@ +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:12:12 + | +LL | fn foo2(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:16:12 + | +LL | fn foo3(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:20:12 + | +LL | fn foo4(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:27:17 + | +LL | fn foo6(_: impl ?Sized + ?Sized) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:31:17 + | +LL | fn foo7(_: impl ?Sized + ?Sized + Debug) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:35:17 + | +LL | fn foo8(_: impl ?Sized + Debug + ?Sized ) {} + | ^^^^^^ ^^^^^^ + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:9:20 + | +LL | fn foo1(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn foo1(a: T) {} +LL + fn foo1(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo1(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:12:29 + | +LL | fn foo2(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn foo2(a: T) {} +LL + fn foo2(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo2(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:16:37 + | +LL | fn foo3(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo3(a: T) {} +LL + fn foo3(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo3(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:20:38 + | +LL | fn foo4(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo4(a: T) {} +LL + fn foo4(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo4(a: &T) {} + | + + +error[E0277]: the size for values of type `impl ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:24:9 + | +LL | fn foo5(_: impl ?Sized) {} + | ^ ----------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider replacing `?Sized` with `Sized` + | +LL - fn foo5(_: impl ?Sized) {} +LL + fn foo5(_: impl Sized) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo5(_: &impl ?Sized) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:27:9 + | +LL | fn foo6(_: impl ?Sized + ?Sized) {} + | ^ -------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo6(_: impl ?Sized + ?Sized) {} +LL + fn foo6(_: impl Sized) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo6(_: &impl ?Sized + ?Sized) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + ?Sized + Debug` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:31:9 + | +LL | fn foo7(_: impl ?Sized + ?Sized + Debug) {} + | ^ ---------------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo7(_: impl ?Sized + ?Sized + Debug) {} +LL + fn foo7(_: impl Debug) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo7(_: &impl ?Sized + ?Sized + Debug) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + Debug + ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:35:9 + | +LL | fn foo8(_: impl ?Sized + Debug + ?Sized ) {} + | ^ ---------------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo8(_: impl ?Sized + Debug + ?Sized ) {} +LL + fn foo8(_: impl Debug ) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo8(_: &impl ?Sized + Debug + ?Sized ) {} + | + + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0203, E0277. +For more information about an error, try `rustc --explain E0203`. From 26fb6a8738f9f991078ad6eb59ee2a9f4f88c649 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 14 Jul 2024 12:19:19 +0200 Subject: [PATCH 100/806] Use statics + clone instead of const until const can access statics --- .../rust-analyzer/crates/hir-def/src/attr.rs | 10 +- .../crates/hir-def/src/body/lower.rs | 42 ++--- .../crates/hir-def/src/builtin_type.rs | 92 +++++----- .../rust-analyzer/crates/hir-def/src/data.rs | 2 +- .../rust-analyzer/crates/hir-def/src/db.rs | 4 +- .../crates/hir-def/src/find_path.rs | 20 +-- .../crates/hir-def/src/item_scope.rs | 2 +- .../crates/hir-def/src/item_tree/lower.rs | 9 +- .../crates/hir-def/src/lang_item.rs | 18 +- .../crates/hir-def/src/nameres/collector.rs | 36 ++-- .../crates/hir-def/src/path/lower.rs | 6 +- .../crates/hir-def/src/resolver.rs | 18 +- .../crates/hir-expand/src/attrs.rs | 77 +++++---- .../crates/hir-expand/src/builtin_fn_macro.rs | 12 +- .../crates/hir-expand/src/declarative.rs | 2 +- .../crates/hir-expand/src/inert_attr_macro.rs | 4 +- .../crates/hir-expand/src/mod_path.rs | 7 +- .../crates/hir-expand/src/name.rs | 17 +- .../crates/hir-ty/src/autoderef.rs | 5 +- .../crates/hir-ty/src/chalk_db.rs | 21 ++- .../crates/hir-ty/src/diagnostics/expr.rs | 4 +- .../crates/hir-ty/src/display.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../crates/hir-ty/src/infer/expr.rs | 4 +- .../crates/hir-ty/src/infer/mutability.rs | 4 +- .../crates/hir-ty/src/infer/path.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/lang_items.rs | 62 ++++--- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 12 +- .../crates/hir-ty/src/mir/eval/shim.rs | 4 +- .../crates/hir-ty/src/mir/lower/as_place.rs | 12 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 6 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +- .../crates/hir/src/source_analyzer.rs | 19 ++- .../src/handlers/convert_bool_then.rs | 2 +- .../handlers/convert_iter_for_each_to_for.rs | 4 +- .../handlers/generate_is_empty_from_len.rs | 4 +- .../ide-assists/src/handlers/inline_call.rs | 2 +- .../crates/ide-completion/src/completions.rs | 3 +- .../ide-completion/src/completions/dot.rs | 6 +- .../ide-completion/src/completions/expr.rs | 4 +- .../src/completions/lifetime.rs | 2 +- .../src/handlers/missing_fields.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 2 +- .../ide/src/syntax_highlighting/highlight.rs | 13 +- .../rust-analyzer/crates/intern/src/symbol.rs | 6 +- .../crates/intern/src/symbol/symbols.rs | 159 +++++++++--------- 49 files changed, 395 insertions(+), 368 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index a1ffb8c0a7596..aacfb07319819 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -9,7 +9,7 @@ use hir_expand::{ attrs::{collect_attrs, Attr, AttrId, RawAttrs}, HirFileId, InFile, }; -use intern::sym; +use intern::{sym, Symbol}; use la_arena::{ArenaMap, Idx, RawIdx}; use mbe::DelimiterKind; use syntax::{ @@ -153,7 +153,7 @@ impl Attrs { } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(LangItem::from_str) + self.by_key("lang").string_value().and_then(|it| LangItem::from_symbol(&Symbol::intern(it))) } pub fn has_doc_hidden(&self) -> bool { @@ -200,7 +200,11 @@ impl Attrs { .segments() .iter() .rev() - .zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev()) + .zip( + [sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()] + .iter() + .rev(), + ) .all(|it| it.0 == it.1) }) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 83ba8f41931e2..b96745022a21a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -188,7 +188,7 @@ impl ExprCollector<'_> { let is_mutable = self_param.mut_token().is_some() && self_param.amp_token().is_none(); let binding_id: la_arena::Idx = self.alloc_binding( - Name::new_symbol_root(sym::self_), + Name::new_symbol_root(sym::self_.clone()), BindingAnnotation::new(is_mutable, false), ); self.body.self_param = Some(binding_id); @@ -1732,14 +1732,14 @@ impl ExprCollector<'_> { let Some(new_v1_formatted) = LangItem::FormatArguments.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::new_v1_formatted), + Name::new_symbol_root(sym::new_v1_formatted.clone()), ) else { return self.missing_expr(); }; let Some(unsafe_arg_new) = LangItem::FormatUnsafeArg.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::new), + Name::new_symbol_root(sym::new.clone()), ) else { return self.missing_expr(); }; @@ -1822,10 +1822,10 @@ impl ExprCollector<'_> { self.db, self.krate, match alignment { - Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left), - Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right), - Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center), - None => Name::new_symbol_root(sym::Unknown), + Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left.clone()), + Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right.clone()), + Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center.clone()), + None => Name::new_symbol_root(sym::Unknown.clone()), }, ); match align { @@ -1851,7 +1851,7 @@ impl ExprCollector<'_> { let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::new), + Name::new_symbol_root(sym::new.clone()), ); match format_placeholder_new { Some(path) => self.alloc_expr_desugared(Expr::Path(path)), @@ -1899,7 +1899,7 @@ impl ExprCollector<'_> { let count_is = match LangItem::FormatCount.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::Is), + Name::new_symbol_root(sym::Is.clone()), ) { Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), None => self.missing_expr(), @@ -1921,7 +1921,7 @@ impl ExprCollector<'_> { let count_param = match LangItem::FormatCount.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::Param), + Name::new_symbol_root(sym::Param.clone()), ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), @@ -1940,7 +1940,7 @@ impl ExprCollector<'_> { None => match LangItem::FormatCount.ty_rel_path( self.db, self.krate, - Name::new_symbol_root(sym::Implied), + Name::new_symbol_root(sym::Implied.clone()), ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), @@ -1963,16 +1963,16 @@ impl ExprCollector<'_> { self.db, self.krate, Name::new_symbol_root(match ty { - Format(Display) => sym::new_display, - Format(Debug) => sym::new_debug, - Format(LowerExp) => sym::new_lower_exp, - Format(UpperExp) => sym::new_upper_exp, - Format(Octal) => sym::new_octal, - Format(Pointer) => sym::new_pointer, - Format(Binary) => sym::new_binary, - Format(LowerHex) => sym::new_lower_hex, - Format(UpperHex) => sym::new_upper_hex, - Usize => sym::from_usize, + Format(Display) => sym::new_display.clone(), + Format(Debug) => sym::new_debug.clone(), + Format(LowerExp) => sym::new_lower_exp.clone(), + Format(UpperExp) => sym::new_upper_exp.clone(), + Format(Octal) => sym::new_octal.clone(), + Format(Pointer) => sym::new_pointer.clone(), + Format(Binary) => sym::new_binary.clone(), + Format(LowerHex) => sym::new_lower_hex.clone(), + Format(UpperHex) => sym::new_upper_hex.clone(), + Usize => sym::from_usize.clone(), }), ) { Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs index 2243f514fe102..6dc1c4546e804 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs @@ -49,63 +49,67 @@ pub enum BuiltinType { impl BuiltinType { #[rustfmt::skip] - pub const ALL: &'static [(Name, BuiltinType)] = &[ - (Name::new_symbol_root(sym::char), BuiltinType::Char), - (Name::new_symbol_root(sym::bool), BuiltinType::Bool), - (Name::new_symbol_root(sym::str), BuiltinType::Str), - - (Name::new_symbol_root(sym::isize), BuiltinType::Int(BuiltinInt::Isize)), - (Name::new_symbol_root(sym::i8), BuiltinType::Int(BuiltinInt::I8)), - (Name::new_symbol_root(sym::i16), BuiltinType::Int(BuiltinInt::I16)), - (Name::new_symbol_root(sym::i32), BuiltinType::Int(BuiltinInt::I32)), - (Name::new_symbol_root(sym::i64), BuiltinType::Int(BuiltinInt::I64)), - (Name::new_symbol_root(sym::i128), BuiltinType::Int(BuiltinInt::I128)), - - (Name::new_symbol_root(sym::usize), BuiltinType::Uint(BuiltinUint::Usize)), - (Name::new_symbol_root(sym::u8), BuiltinType::Uint(BuiltinUint::U8)), - (Name::new_symbol_root(sym::u16), BuiltinType::Uint(BuiltinUint::U16)), - (Name::new_symbol_root(sym::u32), BuiltinType::Uint(BuiltinUint::U32)), - (Name::new_symbol_root(sym::u64), BuiltinType::Uint(BuiltinUint::U64)), - (Name::new_symbol_root(sym::u128), BuiltinType::Uint(BuiltinUint::U128)), - - (Name::new_symbol_root(sym::f16), BuiltinType::Float(BuiltinFloat::F16)), - (Name::new_symbol_root(sym::f32), BuiltinType::Float(BuiltinFloat::F32)), - (Name::new_symbol_root(sym::f64), BuiltinType::Float(BuiltinFloat::F64)), - (Name::new_symbol_root(sym::f128), BuiltinType::Float(BuiltinFloat::F128)), - ]; + pub fn all_builtin_types() -> [(Name, BuiltinType); 19] { + [ + (Name::new_symbol_root(sym::char.clone()), BuiltinType::Char), + (Name::new_symbol_root(sym::bool.clone()), BuiltinType::Bool), + (Name::new_symbol_root(sym::str.clone()), BuiltinType::Str), + + (Name::new_symbol_root(sym::isize.clone()), BuiltinType::Int(BuiltinInt::Isize)), + (Name::new_symbol_root(sym::i8.clone()), BuiltinType::Int(BuiltinInt::I8)), + (Name::new_symbol_root(sym::i16.clone()), BuiltinType::Int(BuiltinInt::I16)), + (Name::new_symbol_root(sym::i32.clone()), BuiltinType::Int(BuiltinInt::I32)), + (Name::new_symbol_root(sym::i64.clone()), BuiltinType::Int(BuiltinInt::I64)), + (Name::new_symbol_root(sym::i128.clone()), BuiltinType::Int(BuiltinInt::I128)), + + (Name::new_symbol_root(sym::usize.clone()), BuiltinType::Uint(BuiltinUint::Usize)), + (Name::new_symbol_root(sym::u8.clone()), BuiltinType::Uint(BuiltinUint::U8)), + (Name::new_symbol_root(sym::u16.clone()), BuiltinType::Uint(BuiltinUint::U16)), + (Name::new_symbol_root(sym::u32.clone()), BuiltinType::Uint(BuiltinUint::U32)), + (Name::new_symbol_root(sym::u64.clone()), BuiltinType::Uint(BuiltinUint::U64)), + (Name::new_symbol_root(sym::u128.clone()), BuiltinType::Uint(BuiltinUint::U128)), + + (Name::new_symbol_root(sym::f16.clone()), BuiltinType::Float(BuiltinFloat::F16)), + (Name::new_symbol_root(sym::f32.clone()), BuiltinType::Float(BuiltinFloat::F32)), + (Name::new_symbol_root(sym::f64.clone()), BuiltinType::Float(BuiltinFloat::F64)), + (Name::new_symbol_root(sym::f128.clone()), BuiltinType::Float(BuiltinFloat::F128)), + ] + } pub fn by_name(name: &Name) -> Option { - Self::ALL.iter().find_map(|(n, ty)| if n == name { Some(*ty) } else { None }) + Self::all_builtin_types() + .iter() + .find_map(|(n, ty)| if n == name { Some(*ty) } else { None }) } } impl AsName for BuiltinType { fn as_name(&self) -> Name { match self { - BuiltinType::Char => Name::new_symbol_root(sym::char), - BuiltinType::Bool => Name::new_symbol_root(sym::bool), - BuiltinType::Str => Name::new_symbol_root(sym::str), + BuiltinType::Char => Name::new_symbol_root(sym::char.clone()), + BuiltinType::Bool => Name::new_symbol_root(sym::bool.clone()), + BuiltinType::Str => Name::new_symbol_root(sym::str.clone()), BuiltinType::Int(it) => match it { - BuiltinInt::Isize => Name::new_symbol_root(sym::isize), - BuiltinInt::I8 => Name::new_symbol_root(sym::i8), - BuiltinInt::I16 => Name::new_symbol_root(sym::i16), - BuiltinInt::I32 => Name::new_symbol_root(sym::i32), - BuiltinInt::I64 => Name::new_symbol_root(sym::i64), - BuiltinInt::I128 => Name::new_symbol_root(sym::i128), + BuiltinInt::Isize => Name::new_symbol_root(sym::isize.clone()), + BuiltinInt::I8 => Name::new_symbol_root(sym::i8.clone()), + BuiltinInt::I16 => Name::new_symbol_root(sym::i16.clone()), + BuiltinInt::I32 => Name::new_symbol_root(sym::i32.clone()), + BuiltinInt::I64 => Name::new_symbol_root(sym::i64.clone()), + BuiltinInt::I128 => Name::new_symbol_root(sym::i128.clone()), }, BuiltinType::Uint(it) => match it { - BuiltinUint::Usize => Name::new_symbol_root(sym::usize), - BuiltinUint::U8 => Name::new_symbol_root(sym::u8), - BuiltinUint::U16 => Name::new_symbol_root(sym::u16), - BuiltinUint::U32 => Name::new_symbol_root(sym::u32), - BuiltinUint::U64 => Name::new_symbol_root(sym::u64), - BuiltinUint::U128 => Name::new_symbol_root(sym::u128), + BuiltinUint::Usize => Name::new_symbol_root(sym::usize.clone()), + BuiltinUint::U8 => Name::new_symbol_root(sym::u8.clone()), + BuiltinUint::U16 => Name::new_symbol_root(sym::u16.clone()), + BuiltinUint::U32 => Name::new_symbol_root(sym::u32.clone()), + BuiltinUint::U64 => Name::new_symbol_root(sym::u64.clone()), + BuiltinUint::U128 => Name::new_symbol_root(sym::u128.clone()), }, BuiltinType::Float(it) => match it { - BuiltinFloat::F16 => Name::new_symbol_root(sym::f16), - BuiltinFloat::F32 => Name::new_symbol_root(sym::f32), - BuiltinFloat::F64 => Name::new_symbol_root(sym::f64), - BuiltinFloat::F128 => Name::new_symbol_root(sym::f128), + BuiltinFloat::F16 => Name::new_symbol_root(sym::f16.clone()), + BuiltinFloat::F32 => Name::new_symbol_root(sym::f32.clone()), + BuiltinFloat::F64 => Name::new_symbol_root(sym::f64.clone()), + BuiltinFloat::F128 => Name::new_symbol_root(sym::f128.clone()), }, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index f60a433c4dafe..964a5f04bfd22 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -485,7 +485,7 @@ impl ExternCrateDeclData { let name = extern_crate.name.clone(); let krate = loc.container.krate(); - let crate_id = if name == sym::self_ { + let crate_id = if name == sym::self_.clone() { Some(krate) } else { db.crate_def_map(krate) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 0a14904f0bceb..eac8f0dd74ac8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -262,8 +262,8 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { let attrs = item_tree.raw_attrs(AttrOwner::TopLevel); for attr in &**attrs { match attr.path().as_ident() { - Some(ident) if *ident == sym::no_std => return true, - Some(ident) if *ident == sym::cfg_attr => {} + Some(ident) if *ident == sym::no_std.clone() => return true, + Some(ident) if *ident == sym::cfg_attr.clone() => {} _ => continue, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index e32add93c5526..a3a602c2c117b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -415,13 +415,13 @@ fn select_best_path( (Unstable, Stable) => return new_path, _ => {} } - const STD_CRATES: [Symbol; 3] = [sym::std, sym::core, sym::alloc]; + let std_crates: [Symbol; 3] = [sym::std.clone(), sym::core.clone(), sym::alloc.clone()]; let choose = |new: (ModPath, _), old: (ModPath, _)| { let (new_path, _) = &new; let (old_path, _) = &old; - let new_has_prelude = new_path.segments().iter().any(|seg| *seg == sym::prelude); - let old_has_prelude = old_path.segments().iter().any(|seg| *seg == sym::prelude); + let new_has_prelude = new_path.segments().iter().any(|seg| *seg == sym::prelude.clone()); + let old_has_prelude = old_path.segments().iter().any(|seg| *seg == sym::prelude.clone()); match (new_has_prelude, old_has_prelude, cfg.prefer_prelude) { (true, false, true) | (false, true, false) => new, (true, false, false) | (false, true, true) => old, @@ -443,19 +443,19 @@ fn select_best_path( match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) - if STD_CRATES.contains(old.symbol()) && STD_CRATES.contains(new.symbol()) => + if std_crates.contains(old.symbol()) && std_crates.contains(new.symbol()) => { let rank = match cfg.prefer_no_std { false => |name: &Name| match name { - name if *name == sym::core => 0, - name if *name == sym::alloc => 1, - name if *name == sym::std => 2, + name if *name == sym::core.clone() => 0, + name if *name == sym::alloc.clone() => 1, + name if *name == sym::std.clone() => 2, _ => unreachable!(), }, true => |name: &Name| match name { - name if *name == sym::core => 2, - name if *name == sym::alloc => 1, - name if *name == sym::std => 0, + name if *name == sym::core.clone() => 2, + name if *name == sym::alloc.clone() => 1, + name if *name == sym::std.clone() => 0, _ => unreachable!(), }, }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index d86c0667a0bb7..092c0a1dfdf81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -119,7 +119,7 @@ struct DeriveMacroInvocation { } pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { - BuiltinType::ALL + BuiltinType::all_builtin_types() .iter() .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None))) .collect() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 496686b4f943c..5c80da93048c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -324,7 +324,8 @@ impl<'a> Ctx<'a> { let self_type = match self_param.ty() { Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), None => { - let self_type = TypeRef::Path(Name::new_symbol_root(sym::Self_).into()); + let self_type = + TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into()); match self_param.kind() { ast::SelfParamKind::Owned => self_type, ast::SelfParamKind::Ref => TypeRef::Reference( @@ -670,7 +671,7 @@ impl<'a> Ctx<'a> { // Traits and trait aliases get the Self type as an implicit first type parameter. generics.type_or_consts.alloc( TypeParamData { - name: Some(Name::new_symbol_root(sym::Self_)), + name: Some(Name::new_symbol_root(sym::Self_.clone())), default: None, provenance: TypeParamProvenance::TraitSelf, } @@ -681,7 +682,7 @@ impl<'a> Ctx<'a> { generics.fill_bounds( &self.body_ctx, bounds, - Either::Left(TypeRef::Path(Name::new_symbol_root(sym::Self_).into())), + Either::Left(TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into())), ); } @@ -746,7 +747,7 @@ fn desugar_future_path(orig: TypeRef) -> Path { let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments().len() - 1).collect(); let binding = AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(sym::Output.clone()), args: None, type_ref: Some(orig), bounds: Box::default(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 4d17650285f40..07b27659ab3dd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -192,8 +192,7 @@ impl LangItems { } pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { - let attrs = db.attrs(item); - attrs.by_key("lang").string_value().and_then(LangItem::from_str) + db.attrs(item).lang_item() } pub(crate) fn notable_traits_in_deps( @@ -261,18 +260,9 @@ macro_rules! language_item_table { } /// Opposite of [`LangItem::name`] - #[allow(clippy::should_implement_trait)] - pub fn from_str(name: &str) -> Option { - match name { - $( stringify!($name) => Some(LangItem::$variant), )* - _ => None, - } - } - - /// Opposite of [`LangItem::name`] - pub fn from_symbol(sym: Symbol) -> Option { + pub fn from_symbol(sym: &Symbol) -> Option { match sym { - $(sym if sym == $module::$name => Some(LangItem::$variant), )* + $(sym if *sym == $module::$name => Some(LangItem::$variant), )* _ => None, } } @@ -283,7 +273,7 @@ macro_rules! language_item_table { impl LangItem { /// Opposite of [`LangItem::name`] pub fn from_name(name: &hir_expand::name::Name) -> Option { - Self::from_str(name.as_str()) + Self::from_symbol(name.symbol()) } pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 30d4a79a808d9..f14679f6c2f14 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -294,24 +294,24 @@ impl DefCollector<'_> { let Some(attr_name) = attr.path.as_ident() else { continue }; match () { - () if *attr_name == sym::recursion_limit => { + () if *attr_name == sym::recursion_limit.clone() => { if let Some(limit) = attr.string_value() { if let Ok(limit) = limit.parse() { crate_data.recursion_limit = Some(limit); } } } - () if *attr_name == sym::crate_type => { + () if *attr_name == sym::crate_type.clone() => { if let Some("proc-macro") = attr.string_value() { self.is_proc_macro = true; } } - () if *attr_name == sym::no_core => crate_data.no_core = true, - () if *attr_name == sym::no_std => crate_data.no_std = true, - () if *attr_name == sym::rustc_coherence_is_core => { + () if *attr_name == sym::no_core.clone() => crate_data.no_core = true, + () if *attr_name == sym::no_std.clone() => crate_data.no_std = true, + () if *attr_name == sym::rustc_coherence_is_core.clone() => { crate_data.rustc_coherence_is_core = true; } - () if *attr_name == sym::feature => { + () if *attr_name == sym::feature.clone() => { let features = attr .parse_path_comma_token_tree(self.db.upcast()) .into_iter() @@ -322,13 +322,13 @@ impl DefCollector<'_> { }); crate_data.unstable_features.extend(features); } - () if *attr_name == sym::register_attr => { + () if *attr_name == sym::register_attr.clone() => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_attrs.push(ident.text.clone()); cov_mark::hit!(register_attr); } } - () if *attr_name == sym::register_tool => { + () if *attr_name == sym::register_tool.clone() => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_tools.push(ident.text.clone()); cov_mark::hit!(register_tool); @@ -538,20 +538,20 @@ impl DefCollector<'_> { } let krate = if self.def_map.data.no_std { - Name::new_symbol_root(sym::core) - } else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std) { - Name::new_symbol_root(sym::std) + Name::new_symbol_root(sym::core.clone()) + } else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std.clone()) { + Name::new_symbol_root(sym::std.clone()) } else { // If `std` does not exist for some reason, fall back to core. This mostly helps // keep r-a's own tests minimal. - Name::new_symbol_root(sym::core) + Name::new_symbol_root(sym::core.clone()) }; let edition = match self.def_map.data.edition { - Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015), - Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018), - Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021), - Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024), + Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015.clone()), + Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018.clone()), + Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021.clone()), + Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024.clone()), }; let path_kind = match self.def_map.data.edition { @@ -560,7 +560,7 @@ impl DefCollector<'_> { }; let path = ModPath::from_segments( path_kind, - [krate, Name::new_symbol_root(sym::prelude), edition], + [krate, Name::new_symbol_root(sym::prelude.clone()), edition], ); let (per_ns, _) = @@ -844,7 +844,7 @@ impl DefCollector<'_> { } fn resolve_extern_crate(&self, name: &Name) -> Option { - if *name == sym::self_ { + if *name == sym::self_.clone() { cov_mark::hit!(extern_crate_self_as); Some(self.def_map.crate_root()) } else { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index 22397df961066..7c39773aa6874 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -60,7 +60,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option { - segments.push(Name::new_symbol_root(sym::Self_)); + segments.push(Name::new_symbol_root(sym::Self_.clone())); } ast::PathSegmentKind::Type { type_ref, trait_ref } => { assert!(path.qualifier().is_none()); // this can only occur at the first segment @@ -268,7 +268,7 @@ fn lower_generic_args_from_fn_path( let bindings = if let Some(ret_type) = ret_type { let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty()); Box::new([AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(sym::Output.clone()), args: None, type_ref: Some(type_ref), bounds: Box::default(), @@ -277,7 +277,7 @@ fn lower_generic_args_from_fn_path( // -> () let type_ref = TypeRef::Tuple(Vec::new()); Box::new([AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(sym::Output.clone()), args: None, type_ref: Some(type_ref), bounds: Box::default(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 4931418546603..c196f00281639 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -194,12 +194,12 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if *first_name == sym::Self_ { + if *first_name == sym::Self_.clone() { return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); } } &Scope::AdtScope(adt) => { - if *first_name == sym::Self_ { + if *first_name == sym::Self_.clone() { return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); } } @@ -291,7 +291,7 @@ impl Resolver { } }; let n_segments = path.segments().len(); - let tmp = Name::new_symbol_root(sym::Self_); + let tmp = Name::new_symbol_root(sym::self_.clone()); let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); if skip_to_mod { @@ -322,7 +322,7 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if *first_name == sym::Self_ { + if *first_name == sym::Self_.clone() { return Some(ResolveValueResult::ValueNs( ValueNs::ImplSelf(impl_), None, @@ -349,7 +349,7 @@ impl Resolver { } } &Scope::ImplDefScope(impl_) => { - if *first_name == sym::Self_ { + if *first_name == sym::Self_.clone() { return Some(ResolveValueResult::Partial( TypeNs::SelfType(impl_), 1, @@ -358,7 +358,7 @@ impl Resolver { } } Scope::AdtScope(adt) => { - if *first_name == sym::Self_ { + if *first_name == sym::Self_.clone() { let ty = TypeNs::AdtSelfType(*adt); return Some(ResolveValueResult::Partial(ty, 1, None)); } @@ -422,7 +422,7 @@ impl Resolver { } pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { - if lifetime.name == sym::tick_static { + if lifetime.name == sym::tick_static.clone() { return Some(LifetimeNs::Static); } @@ -778,10 +778,10 @@ impl Scope { } } Scope::ImplDefScope(i) => { - acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::ImplSelfType(*i)); + acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::ImplSelfType(*i)); } Scope::AdtScope(i) => { - acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::AdtSelfType(*i)); + acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::AdtSelfType(*i)); } Scope::ExprScope(scope) => { if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 6afa7a40f123f..36636a228f147 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -59,7 +59,10 @@ impl RawAttrs { text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))), span, }))), - path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))), + path: Interned::new(ModPath::from(Name::new_symbol( + sym::doc.clone(), + span.ctx, + ))), ctxt: span.ctx, } }), @@ -116,47 +119,47 @@ impl RawAttrs { pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs { let has_cfg_attrs = self .iter() - .any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr)); + .any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone())); if !has_cfg_attrs { return self; } let crate_graph = db.crate_graph(); - let new_attrs = self - .iter() - .flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } - - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; + let new_attrs = + self.iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = + attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone()); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); - - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); - - attrs.collect() - } - }) - .collect::>(); + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; + + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( + |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), + ); + + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); + + attrs.collect() + } + }) + .collect::>(); let entries = if new_attrs.is_empty() { None } else { @@ -384,7 +387,7 @@ impl Attr { } pub fn cfg(&self) -> Option { - if *self.path.as_ident()? == sym::cfg { + if *self.path.as_ident()? == sym::cfg.clone() { self.token_tree_value().map(CfgExpr::parse) } else { None diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index e5de5975a2d8b..97867dfc66e92 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -367,7 +367,11 @@ fn panic_expand( let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); - let mac = if use_panic_2021(db, call_site_span) { sym::panic_2021 } else { sym::panic_2015 }; + let mac = if use_panic_2021(db, call_site_span) { + sym::panic_2021.clone() + } else { + sym::panic_2015.clone() + }; // Expand to a macro call `$crate::panic::panic_{edition}` let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); @@ -396,9 +400,9 @@ fn unreachable_expand( let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = if use_panic_2021(db, call_site_span) { - sym::unreachable_2021 + sym::unreachable_2021.clone() } else { - sym::unreachable_2015 + sym::unreachable_2015.clone() }; // Expand to a macro call `$crate::panic::panic_{edition}` @@ -431,7 +435,7 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { // FIXME: Record allow_internal_unstable in the macro def (not been done yet because it // would consume quite a bit extra memory for all call locs...) // if let Some(features) = expn.def.allow_internal_unstable { - // if features.iter().any(|&f| f == sym::edition_panic) { + // if features.iter().any(|&f| f == sym::edition_panic.clone()) { // span = expn.call_site; // continue; // } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index b920d2127abe8..2e7865ed3bf8c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -114,7 +114,7 @@ impl DeclarativeMacroExpander { .find(|it| { it.path .as_ident() - .map(|it| *it == sym::rustc_macro_transparency) + .map(|it| *it == sym::rustc_macro_transparency.clone()) .unwrap_or(false) })? .token_tree_value()? diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs index 7ead7e9390152..0c112554e1f43 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs @@ -553,7 +553,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), BuiltinAttribute { - // name: sym::rustc_diagnostic_item, + // name: sym::rustc_diagnostic_item.clone(), name: "rustc_diagnostic_item", // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. // only_local: false, @@ -562,7 +562,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // duplicates: ErrorFollowing, // gate: Gated( // Stability::Unstable, - // sym::rustc_attrs, + // sym::rustc_attrs.clone(), // "diagnostic items compiler internal support for linting", // cfg_fn!(rustc_attrs), // ), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 7ff8b797fa4a6..ed7d551888756 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -120,7 +120,8 @@ impl ModPath { #[allow(non_snake_case)] pub fn is_Self(&self) -> bool { - self.kind == PathKind::Plain && matches!(&*self.segments, [name] if *name == sym::Self_) + self.kind == PathKind::Plain + && matches!(&*self.segments, [name] if *name == sym::Self_.clone()) } /// If this path is a single identifier, like `foo`, return its name. @@ -264,7 +265,7 @@ fn convert_path( } ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments( PathKind::Plain, - Some(Name::new_symbol(sym::Self_, SyntaxContextId::ROOT)), + Some(Name::new_symbol(sym::Self_.clone(), SyntaxContextId::ROOT)), ), ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, @@ -396,7 +397,7 @@ macro_rules! __path { ($start:ident $(:: $seg:ident)*) => ({ $crate::__known_path!($start $(:: $seg)*); $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![ - $crate::name::Name::new_symbol_root(intern::sym::$start), $($crate::name::Name::new_symbol_root(intern::sym::$seg),)* + $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)* ]) }); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index da0adbef16342..67e73f7fc284c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -2,7 +2,7 @@ use std::fmt; -use intern::{sym::MISSING_NAME, Symbol}; +use intern::{sym, Symbol}; use span::SyntaxContextId; use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr}; @@ -13,12 +13,21 @@ use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr}; /// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it /// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the /// name without "r#". -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Name { symbol: Symbol, ctx: (), } +impl fmt::Debug for Name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Name") + .field("symbol", &self.symbol.as_str()) + .field("ctx", &self.ctx) + .finish() + } +} + impl Ord for Name { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.symbol.as_str().cmp(other.symbol.as_str()) @@ -116,8 +125,8 @@ impl Name { /// Ideally, we want a `gensym` semantics for missing names -- each missing /// name is equal only to itself. It's not clear how to implement this in /// salsa though, so we punt on that bit for a moment. - pub const fn missing() -> Name { - Name { symbol: MISSING_NAME, ctx: () } + pub fn missing() -> Name { + Name { symbol: sym::MISSING_NAME.clone(), ctx: () } } /// Returns true if this is a fake name for things missing in the source code. See diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 736379a3d8730..ecfc1ff99e9ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -152,8 +152,9 @@ pub(crate) fn deref_by_trait( let deref_trait = db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; - let target = - db.trait_data(deref_trait).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; + let target = db + .trait_data(deref_trait) + .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?; let projection = { let b = TyBuilder::subst_for_def(db, deref_trait, None); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 868827b2b6603..d506e00ca1239 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -289,17 +289,16 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { chalk_ir::Binders::new(binders, bound) } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { - if let Some((future_trait, future_output)) = self - .db - .lang_item(self.krate, LangItem::Future) - .and_then(|item| item.as_trait()) - .and_then(|trait_| { - let alias = self - .db - .trait_data(trait_) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - Some((trait_, alias)) - }) + if let Some((future_trait, future_output)) = + self.db + .lang_item(self.krate, LangItem::Future) + .and_then(|item| item.as_trait()) + .and_then(|trait_| { + let alias = self.db.trait_data(trait_).associated_type_by_name( + &Name::new_symbol_root(sym::Output.clone()), + )?; + Some((trait_, alias)) + }) { // Making up Symbol’s value as variable is void: AsyncBlock: // diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 048bdd2c38bce..e52fae06d7f73 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -423,7 +423,9 @@ impl FilterMapNextChecker { ItemContainerId::TraitId(iterator_trait_id) => { let iterator_trait_items = &db.trait_data(iterator_trait_id).items; iterator_trait_items.iter().find_map(|(name, it)| match it { - &AssocItemId::FunctionId(id) if *name == sym::filter_map => Some(id), + &AssocItemId::FunctionId(id) if *name == sym::filter_map.clone() => { + Some(id) + } _ => None, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 2a644de1fbecc..f15f6575b7904 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1171,8 +1171,9 @@ impl HirDisplay for Ty { .lang_item(body.module(db.upcast()).krate(), LangItem::Future) .and_then(LangItemTarget::as_trait); let output = future_trait.and_then(|t| { - db.trait_data(t) - .associated_type_by_name(&Name::new_symbol_root(sym::Output)) + db.trait_data(t).associated_type_by_name(&Name::new_symbol_root( + sym::Output.clone(), + )) }); write!(f, "impl ")?; if let Some(t) = future_trait { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 928a3f5e493c7..82f4ad01e0189 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1425,7 +1425,9 @@ impl<'a> InferenceContext<'a> { } fn resolve_output_on(&self, trait_: TraitId) -> Option { - self.db.trait_data(trait_).associated_type_by_name(&Name::new_symbol_root(sym::Output)) + self.db + .trait_data(trait_) + .associated_type_by_name(&Name::new_symbol_root(sym::Output.clone())) } fn resolve_lang_trait(&self, lang: LangItem) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index f54081a276c8d..417fca5dcd671 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -623,7 +623,7 @@ impl InferenceContext<'_> { if let Some(deref_fn) = self .db .trait_data(deref_trait) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone())) { break 'b deref_fn == f; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a0c3f48642e3b..7857d207be7cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -650,7 +650,7 @@ impl InferenceContext<'_> { if let Some(deref_fn) = self .db .trait_data(deref_trait) - .method_by_name(&Name::new_symbol_root(sym::deref)) + .method_by_name(&Name::new_symbol_root(sym::deref.clone())) { // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that // the mutability is not wrong, and will be fixed in `self.infer_mut`). @@ -791,7 +791,7 @@ impl InferenceContext<'_> { if let Some(func) = self .db .trait_data(index_trait) - .method_by_name(&Name::new_symbol_root(sym::index)) + .method_by_name(&Name::new_symbol_root(sym::index.clone())) { let substs = TyBuilder::subst_for_def(self.db, index_trait, None) .push(self_ty.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index ca8996fb89e77..abb702d15002f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -112,7 +112,7 @@ impl InferenceContext<'_> { if let Some(index_fn) = self .db .trait_data(index_trait) - .method_by_name(&Name::new_symbol_root(sym::index_mut)) + .method_by_name(&Name::new_symbol_root(sym::index_mut.clone())) { *f = index_fn; let base_adjustments = self @@ -145,7 +145,7 @@ impl InferenceContext<'_> { if let Some(deref_fn) = self .db .trait_data(deref_trait) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone())) { *f = deref_fn; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 67a0ec60f19d6..0b44bbec70f75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -228,7 +228,7 @@ impl InferenceContext<'_> { Path::LangItem(..) => ( PathSegment { name: { - _d = Name::new_symbol_root(sym::Unknown); + _d = Name::new_symbol_root(sym::Unknown.clone()); &_d }, args_and_bindings: None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 800b06afbd0b2..7ee63af1c229e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -783,7 +783,7 @@ impl<'a> InferenceTable<'a> { let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let trait_data = self.db.trait_data(fn_once_trait); let output_assoc_type = - trait_data.associated_type_by_name(&Name::new_symbol_root(sym::Output))?; + trait_data.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?; let mut arg_tys = Vec::with_capacity(num_args); let arg_ty = TyBuilder::tuple(num_args) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index 69ff03eb49ece..f704b59d303e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -21,43 +21,53 @@ pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangIte Some(match op { BinaryOp::LogicOp(_) => return None, BinaryOp::ArithOp(aop) => match aop { - ArithOp::Add => (Name::new_symbol_root(sym::add), LangItem::Add), - ArithOp::Mul => (Name::new_symbol_root(sym::mul), LangItem::Mul), - ArithOp::Sub => (Name::new_symbol_root(sym::sub), LangItem::Sub), - ArithOp::Div => (Name::new_symbol_root(sym::div), LangItem::Div), - ArithOp::Rem => (Name::new_symbol_root(sym::rem), LangItem::Rem), - ArithOp::Shl => (Name::new_symbol_root(sym::shl), LangItem::Shl), - ArithOp::Shr => (Name::new_symbol_root(sym::shr), LangItem::Shr), - ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor), LangItem::BitXor), - ArithOp::BitOr => (Name::new_symbol_root(sym::bitor), LangItem::BitOr), - ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand), LangItem::BitAnd), + ArithOp::Add => (Name::new_symbol_root(sym::add.clone()), LangItem::Add), + ArithOp::Mul => (Name::new_symbol_root(sym::mul.clone()), LangItem::Mul), + ArithOp::Sub => (Name::new_symbol_root(sym::sub.clone()), LangItem::Sub), + ArithOp::Div => (Name::new_symbol_root(sym::div.clone()), LangItem::Div), + ArithOp::Rem => (Name::new_symbol_root(sym::rem.clone()), LangItem::Rem), + ArithOp::Shl => (Name::new_symbol_root(sym::shl.clone()), LangItem::Shl), + ArithOp::Shr => (Name::new_symbol_root(sym::shr.clone()), LangItem::Shr), + ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor.clone()), LangItem::BitXor), + ArithOp::BitOr => (Name::new_symbol_root(sym::bitor.clone()), LangItem::BitOr), + ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand.clone()), LangItem::BitAnd), }, BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (Name::new_symbol_root(sym::add_assign), LangItem::AddAssign), - ArithOp::Mul => (Name::new_symbol_root(sym::mul_assign), LangItem::MulAssign), - ArithOp::Sub => (Name::new_symbol_root(sym::sub_assign), LangItem::SubAssign), - ArithOp::Div => (Name::new_symbol_root(sym::div_assign), LangItem::DivAssign), - ArithOp::Rem => (Name::new_symbol_root(sym::rem_assign), LangItem::RemAssign), - ArithOp::Shl => (Name::new_symbol_root(sym::shl_assign), LangItem::ShlAssign), - ArithOp::Shr => (Name::new_symbol_root(sym::shr_assign), LangItem::ShrAssign), - ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor_assign), LangItem::BitXorAssign), - ArithOp::BitOr => (Name::new_symbol_root(sym::bitor_assign), LangItem::BitOrAssign), - ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand_assign), LangItem::BitAndAssign), + ArithOp::Add => (Name::new_symbol_root(sym::add_assign.clone()), LangItem::AddAssign), + ArithOp::Mul => (Name::new_symbol_root(sym::mul_assign.clone()), LangItem::MulAssign), + ArithOp::Sub => (Name::new_symbol_root(sym::sub_assign.clone()), LangItem::SubAssign), + ArithOp::Div => (Name::new_symbol_root(sym::div_assign.clone()), LangItem::DivAssign), + ArithOp::Rem => (Name::new_symbol_root(sym::rem_assign.clone()), LangItem::RemAssign), + ArithOp::Shl => (Name::new_symbol_root(sym::shl_assign.clone()), LangItem::ShlAssign), + ArithOp::Shr => (Name::new_symbol_root(sym::shr_assign.clone()), LangItem::ShrAssign), + ArithOp::BitXor => { + (Name::new_symbol_root(sym::bitxor_assign.clone()), LangItem::BitXorAssign) + } + ArithOp::BitOr => { + (Name::new_symbol_root(sym::bitor_assign.clone()), LangItem::BitOrAssign) + } + ArithOp::BitAnd => { + (Name::new_symbol_root(sym::bitand_assign.clone()), LangItem::BitAndAssign) + } }, BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (Name::new_symbol_root(sym::eq), LangItem::PartialEq), - CmpOp::Eq { negated: true } => (Name::new_symbol_root(sym::ne), LangItem::PartialEq), + CmpOp::Eq { negated: false } => { + (Name::new_symbol_root(sym::eq.clone()), LangItem::PartialEq) + } + CmpOp::Eq { negated: true } => { + (Name::new_symbol_root(sym::ne.clone()), LangItem::PartialEq) + } CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (Name::new_symbol_root(sym::le), LangItem::PartialOrd) + (Name::new_symbol_root(sym::le.clone()), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (Name::new_symbol_root(sym::lt), LangItem::PartialOrd) + (Name::new_symbol_root(sym::lt.clone()), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (Name::new_symbol_root(sym::ge), LangItem::PartialOrd) + (Name::new_symbol_root(sym::ge.clone()), LangItem::PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (Name::new_symbol_root(sym::gt), LangItem::PartialOrd) + (Name::new_symbol_root(sym::gt.clone()), LangItem::PartialOrd) } }, BinaryOp::Assignment { op: None } => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 60447ac582030..aabf11f268ff8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -897,7 +897,7 @@ pub fn callable_sig_from_fn_trait( let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db .trait_data(fn_once_trait) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; + .associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?; let mut table = InferenceTable::new(db, trait_env.clone()); let b = TyBuilder::trait_ref(db, fn_once_trait); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 982080c5ff5fe..32b7d6dc1138a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -631,18 +631,20 @@ impl Evaluator<'_> { cached_fn_trait_func: db .lang_item(crate_id, LangItem::Fn) .and_then(|x| x.as_trait()) - .and_then(|x| db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call))), + .and_then(|x| { + db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call.clone())) + }), cached_fn_mut_trait_func: db .lang_item(crate_id, LangItem::FnMut) .and_then(|x| x.as_trait()) .and_then(|x| { - db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_mut)) + db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_mut.clone())) }), cached_fn_once_trait_func: db .lang_item(crate_id, LangItem::FnOnce) .and_then(|x| x.as_trait()) .and_then(|x| { - db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_once)) + db.trait_data(x).method_by_name(&Name::new_symbol_root(sym::call_once.clone())) }), }) } @@ -2694,7 +2696,7 @@ impl Evaluator<'_> { ) -> Result<()> { let Some(drop_fn) = (|| { let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?; - self.db.trait_data(drop_trait).method_by_name(&Name::new_symbol_root(sym::drop)) + self.db.trait_data(drop_trait).method_by_name(&Name::new_symbol_root(sym::drop.clone())) })() else { // in some tests we don't have drop trait in minicore, and // we can ignore drop in them. @@ -2803,7 +2805,7 @@ pub fn render_const_using_debug_impl( not_supported!("core::fmt::Debug not found"); }; let Some(debug_fmt_fn) = - db.trait_data(debug_trait).method_by_name(&Name::new_symbol_root(sym::fmt)) + db.trait_data(debug_trait).method_by_name(&Name::new_symbol_root(sym::fmt.clone())) else { not_supported!("core::fmt::Debug::fmt not found"); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 67e102b1ed183..6818759310613 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -319,7 +319,7 @@ impl Evaluator<'_> { return Some(LangItem::BeginPanic); } - let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?; + let candidate = attrs.lang_item()?; // We want to execute these functions with special logic // `PanicFmt` is not detected here as it's redirected later. if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { @@ -1279,7 +1279,7 @@ impl Evaluator<'_> { if let Some(def) = target.as_trait().and_then(|it| { self.db .trait_data(it) - .method_by_name(&Name::new_symbol_root(sym::call_once)) + .method_by_name(&Name::new_symbol_root(sym::call_once.clone())) }) { self.exec_fn_trait( def, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 5e8a2dfb735f6..424ee1160c820 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -189,10 +189,10 @@ impl MirLowerCtx<'_> { if let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut)?.as_trait() { - if let Some(deref_fn) = self - .db - .trait_data(deref_trait) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name( + &Name::new_symbol_root(sym::deref_mut.clone()), + ) { break 'b deref_fn == f; } @@ -327,14 +327,14 @@ impl MirLowerCtx<'_> { ( Mutability::Not, LangItem::Deref, - Name::new_symbol_root(sym::deref), + Name::new_symbol_root(sym::deref.clone()), BorrowKind::Shared, ) } else { ( Mutability::Mut, LangItem::DerefMut, - Name::new_symbol_root(sym::deref_mut), + Name::new_symbol_root(sym::deref_mut.clone()), BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index ce9ffa284c904..c46382a0ea822 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -257,9 +257,9 @@ impl FnTrait { pub fn method_name(self) -> Name { match self { - FnTrait::FnOnce => Name::new_symbol_root(sym::call_once), - FnTrait::FnMut => Name::new_symbol_root(sym::call_mut), - FnTrait::Fn => Name::new_symbol_root(sym::call), + FnTrait::FnOnce => Name::new_symbol_root(sym::call_once.clone()), + FnTrait::FnMut => Name::new_symbol_root(sym::call_mut.clone()), + FnTrait::Fn => Name::new_symbol_root(sym::call.clone()), } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f90656c75f590..c868357ff982f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1827,7 +1827,7 @@ impl DefWithBody { continue; } let mut need_mut = &mol[local]; - if body[binding_id].name == sym::self_ + if body[binding_id].name == sym::self_.clone() && need_mut == &mir::MutabilityReason::Unused { need_mut = &mir::MutabilityReason::Not; @@ -2589,7 +2589,7 @@ pub struct StaticLifetime; impl StaticLifetime { pub fn name(self) -> Name { - Name::new_symbol_root(sym::tick_static) + Name::new_symbol_root(sym::tick_static.clone()) } } @@ -3249,7 +3249,7 @@ impl Local { } pub fn is_self(self, db: &dyn HirDatabase) -> bool { - self.name(db) == sym::self_ + self.name(db) == sym::self_.clone() } pub fn is_mut(self, db: &dyn HirDatabase) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 45401702e9f00..be0116862b9cf 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -366,7 +366,7 @@ impl SourceAnalyzer { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { AssocItem::TypeAlias(alias) - if alias.name(db) == Name::new_symbol_root(sym::IntoFuture) => + if alias.name(db) == Name::new_symbol_root(sym::IntoFuture.clone()) => { Some(alias) } @@ -395,8 +395,11 @@ impl SourceAnalyzer { // This can be either `Deref::deref` or `DerefMut::deref_mut`. // Since deref kind is inferenced and stored in `InferenceResult.method_resolution`, // use that result to find out which one it is. - let (deref_trait, deref) = - self.lang_trait_fn(db, LangItem::Deref, &Name::new_symbol_root(sym::deref))?; + let (deref_trait, deref) = self.lang_trait_fn( + db, + LangItem::Deref, + &Name::new_symbol_root(sym::deref.clone()), + )?; self.infer .as_ref() .and_then(|infer| { @@ -405,7 +408,7 @@ impl SourceAnalyzer { let (deref_mut_trait, deref_mut) = self.lang_trait_fn( db, LangItem::DerefMut, - &Name::new_symbol_root(sym::deref_mut), + &Name::new_symbol_root(sym::deref_mut.clone()), )?; if func == deref_mut { Some((deref_mut_trait, deref_mut)) @@ -416,10 +419,10 @@ impl SourceAnalyzer { .unwrap_or((deref_trait, deref)) } ast::UnaryOp::Not => { - self.lang_trait_fn(db, LangItem::Not, &Name::new_symbol_root(sym::not))? + self.lang_trait_fn(db, LangItem::Not, &Name::new_symbol_root(sym::not.clone()))? } ast::UnaryOp::Neg => { - self.lang_trait_fn(db, LangItem::Neg, &Name::new_symbol_root(sym::neg))? + self.lang_trait_fn(db, LangItem::Neg, &Name::new_symbol_root(sym::neg.clone()))? } }; @@ -441,7 +444,7 @@ impl SourceAnalyzer { let index_ty = self.ty_of_expr(db, &index_expr.index()?)?; let (index_trait, index_fn) = - self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index))?; + self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index.clone()))?; let (op_trait, op_fn) = self .infer .as_ref() @@ -451,7 +454,7 @@ impl SourceAnalyzer { let (index_mut_trait, index_mut_fn) = self.lang_trait_fn( db, LangItem::IndexMut, - &Name::new_symbol_root(sym::index_mut), + &Name::new_symbol_root(sym::index_mut.clone()), )?; if func == index_mut_fn { Some((index_mut_trait, index_mut_fn)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 27e39f6f8ab26..77f9c66b354ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -223,7 +223,7 @@ fn option_variants( let fam = FamousDefs(sema, sema.scope(expr)?.krate()); let option_variants = fam.core_option_Option()?.variants(sema.db); match &*option_variants { - &[variant0, variant1] => Some(if variant0.name(sema.db) == sym::None { + &[variant0, variant1] => Some(if variant0.name(sema.db) == sym::None.clone() { (variant0, variant1) } else { (variant1, variant0) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index d85147e84b20c..e86ff0dbebc62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -150,9 +150,9 @@ fn is_ref_and_impls_iter_method( _ => return None, }; let wanted_method = Name::new_symbol_root(if ref_expr.mut_token().is_some() { - sym::iter_mut + sym::iter_mut.clone() } else { - sym::iter + sym::iter.clone() }); let expr_behind_ref = ref_expr.expr()?; let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index 05210e66ac24d..ad422b25c39e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -54,13 +54,13 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext< } let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?; - let len_fn = get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::len))?; + let len_fn = get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::len.clone()))?; if !len_fn.ret_type(ctx.sema.db).is_usize() { cov_mark::hit!(len_fn_different_return_type); return None; } - if get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::is_empty)).is_some() { + if get_impl_method(ctx, &impl_, &Name::new_symbol_root(sym::is_empty.clone())).is_some() { cov_mark::hit!(is_empty_already_implemented); return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 137b8d13171fe..d0382499b996a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -430,7 +430,7 @@ fn inline( let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty); - let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_); + let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_.clone()); if is_self { let mut this_pat = make::ident_pat(false, false, make::name("this")); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 4a8ea171805e2..ee27d8611eb80 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -618,7 +618,8 @@ fn enum_variants_with_paths( let mut process_variant = |variant: Variant| { let self_path = hir::ModPath::from_segments( hir::PathKind::Plain, - iter::once(Name::new_symbol_root(sym::Self_)).chain(iter::once(variant.name(ctx.db))), + iter::once(Name::new_symbol_root(sym::Self_.clone())) + .chain(iter::once(variant.name(ctx.db))), ); cb(acc, ctx, variant, self_path); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d3290db4ef82b..a07daf4c4e49c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -91,13 +91,13 @@ pub(crate) fn complete_undotted_self( in_breakable: expr_ctx.in_breakable, }, }, - Some(Name::new_symbol_root(sym::self_)), + Some(Name::new_symbol_root(sym::self_.clone())), field, &ty, ) }, |acc, field, ty| { - acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_)), field, &ty) + acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_.clone())), field, &ty) }, true, false, @@ -115,7 +115,7 @@ pub(crate) fn complete_undotted_self( }, }, func, - Some(Name::new_symbol_root(sym::self_)), + Some(Name::new_symbol_root(sym::self_.clone())), None, ) }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 72b36922da0bd..71ff6b5aea37a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -190,7 +190,7 @@ pub(crate) fn complete_expr_path( path_ctx, strukt, None, - Some(Name::new_symbol_root(sym::Self_)), + Some(Name::new_symbol_root(sym::Self_.clone())), ); } } @@ -214,7 +214,7 @@ pub(crate) fn complete_expr_path( ctx, un, None, - Some(Name::new_symbol_root(sym::Self_)), + Some(Name::new_symbol_root(sym::Self_.clone())), ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 541ccf2d198e3..f31352f49f9de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs @@ -47,7 +47,7 @@ pub(crate) fn complete_lifetime( } }); if param_lifetime.is_none() { - acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static)); + acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone())); } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 4cdb279a21ca6..a41bf457a9032 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -210,7 +210,7 @@ fn get_default_constructor( let has_new_func = ty .iterate_assoc_items(ctx.sema.db, krate, |assoc_item| { if let AssocItem::Function(func) = assoc_item { - if func.name(ctx.sema.db) == sym::new + if func.name(ctx.sema.db) == sym::new.clone() && func.assoc_fn_params(ctx.sema.db).is_empty() { return Some(()); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 0692f0d700612..ce1a56e45062b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -633,7 +633,7 @@ fn hint_iterator( if ty.impls_trait(db, iter_trait, &[]) { let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { - hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item => Some(alias), + hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item.clone() => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index c73b6acb0d073..291073f87735c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -6,6 +6,7 @@ use ide_db::{ defs::{Definition, IdentClass, NameClass, NameRefClass}, FxHashMap, RootDatabase, SymbolKind, }; +use stdx::hash_once; use syntax::{ ast, match_ast, AstNode, AstToken, NodeOrToken, SyntaxKind::{self, *}, @@ -358,17 +359,7 @@ fn highlight_name( } fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { - fn hash(x: T) -> u64 { - use ide_db::FxHasher; - - use std::hash::Hasher; - - let mut hasher = FxHasher::default(); - x.hash(&mut hasher); - hasher.finish() - } - - hash((name, shadow_count)) + hash_once::((name.as_str(), shadow_count)) } pub(super) fn highlight_def( diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 9f7a788d006c8..9e275713876c0 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -91,7 +91,7 @@ impl TaggedArcPtr { } #[inline] - const fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { + fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { let packed_tag = true as usize; // can't use this strict provenance stuff here due to trait methods not being const @@ -112,7 +112,7 @@ impl TaggedArcPtr { // } // so what follows is roughly what the above looks like but inlined - let self_addr = unsafe { core::mem::transmute::<*const _, usize>(ptr.as_ptr()) }; + let self_addr = ptr.as_ptr() as *const *const str as usize; let addr = self_addr | packed_tag; let dest_addr = addr as isize; let offset = dest_addr.wrapping_sub(self_addr as isize); @@ -222,7 +222,7 @@ impl Symbol { .try_as_arc_owned() .unwrap(), ); - debug_assert_eq!(Arc::count(&arc), 1); + debug_assert_eq!(Arc::count(arc), 1); // Shrink the backing storage if the shard is less than 50% occupied. if shard.len() * 2 < shard.capacity() { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index af1af0536c2e5..6304155ed76ad 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -14,10 +14,10 @@ use crate::{ macro_rules! define_symbols { (@WITH_NAME: $($alias:ident = $value:literal),* $(,)? @PLAIN: $($name:ident),* $(,)?) => { $( - pub const $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; + pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; )* $( - pub const $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) }; + pub static $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) }; )* @@ -78,10 +78,13 @@ define_symbols! { @PLAIN: add_assign, add, + align_offset, + alloc_layout, alloc, as_str, asm, assert, + begin_panic, bench, bitand_assign, bitand, @@ -89,88 +92,13 @@ define_symbols! { bitor, bitxor_assign, bitxor, - transmute_opts, - transmute_trait, - coerce_unsized, - dispatch_from_dyn,destruct, bool, - panic, - begin_panic, - panic_nounwind, - panic_fmt, - panic_misaligned_pointer_dereference, - panic_display, - const_panic_fmt, - panic_bounds_check, - panic_info, - panic_location, - panic_impl, - panic_cannot_unwind, - sized, - unsize, - format_alignment, - start, - format_argument, - format_arguments, - format_count, - format_placeholder, - format_unsafe_arg, - exchange_malloc, box_free, - drop_in_place, - alloc_layout, - eh_personality, - eh_catch_typeinfo, - phantom_data, - manually_drop, - maybe_uninit, - align_offset, - termination, - tuple_trait, - slice_len_fn, - from_residual, - from_output, - from_yeet, - pointer_like, - const_param_ty, - Poll, - Ready, - Pending, - ResumeTy, - get_context, - Context, - Some, - Err, - Continue, - Break, - into_iter, - new_unchecked, - range_inclusive_new, - CStr, - fn_ptr_trait, - freeze, - coroutine_state, - c_void, - coroutine, - unpin, - pin, - fn_ptr_addr, - structural_teq, - fn_once_output, - copy, - clone, - sync, - discriminant_kind, Box, - structural_peq, boxed, branch, - discriminant_type, - pointee_trait, - metadata_type, - dyn_metadata, - deref_target, - receiver, + Break, + c_void, call_mut, call_once, call, @@ -180,31 +108,51 @@ define_symbols! { cfg_eval, cfg, char, + clone, Clone, + coerce_unsized, column, compile_error, concat_bytes, concat_idents, concat, const_format_args, + const_panic_fmt, + const_param_ty, + Context, + Continue, + copy, Copy, core_panic, core, + coroutine_state, + coroutine, crate_type, + CStr, Debug, default, Default, deref_mut, + deref_target, deref, derive_const, derive, + discriminant_kind, + discriminant_type, + dispatch_from_dyn,destruct, div_assign, div, doc, + drop_in_place, drop, + dyn_metadata, + eh_catch_typeinfo, + eh_personality, env, eq, Eq, + Err, + exchange_malloc, f128, f16, f32, @@ -214,15 +162,29 @@ define_symbols! { filter_map, fmt, fn_mut, + fn_once_output, fn_once, + fn_ptr_addr, + fn_ptr_trait, + format_alignment, format_args_nl, format_args, + format_argument, + format_arguments, + format_count, + format_placeholder, + format_unsafe_arg, format, + freeze, + from_output, + from_residual, from_usize, + from_yeet, future_trait, future, Future, ge, + get_context, global_allocator, global_asm, gt, @@ -240,6 +202,7 @@ define_symbols! { index, Index, into_future, + into_iter, IntoFuture, IntoIter, IntoIterator, @@ -258,6 +221,9 @@ define_symbols! { log_syntax, lt, macro_rules, + manually_drop, + maybe_uninit, + metadata_type, module_path, mul_assign, mul, @@ -271,6 +237,7 @@ define_symbols! { new_lower_hex, new_octal, new_pointer, + new_unchecked, new_upper_exp, new_upper_hex, new_v1_formatted, @@ -293,21 +260,39 @@ define_symbols! { owned_box, panic_2015, panic_2021, + panic_bounds_check, + panic_cannot_unwind, + panic_display, + panic_fmt, + panic_impl, + panic_info, + panic_location, + panic_misaligned_pointer_dereference, + panic_nounwind, + panic, Param, partial_ord, PartialEq, PartialOrd, + Pending, + phantom_data, pieces, + pin, + pointee_trait, + pointer_like, poll, + Poll, prelude, quote, - r#fn, + range_inclusive_new, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, + Ready, + receiver, recursion_limit, register_attr, register_tool, @@ -315,6 +300,7 @@ define_symbols! { rem, result, Result, + ResumeTy, Right, rust_2015, rust_2018, @@ -327,30 +313,43 @@ define_symbols! { shl, shr_assign, shr, + sized, + slice_len_fn, + Some, + start, std_panic, std, str, string, String, stringify, + structural_peq, + structural_teq, sub_assign, sub, + sync, Target, + termination, test_case, test, trace_macros, + transmute_opts, + transmute_trait, transparent, Try, + tuple_trait, u128, u16, u32, u64, u8, Unknown, + unpin, unreachable_2015, unreachable_2021, unreachable, unsafe_cell, + unsize, usize, v1, va_list From 185f4b06a7d04255834aecbaa4f1fd33b0db04f1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 14 Jul 2024 14:52:36 -0400 Subject: [PATCH 101/806] Fix trivial gen ident usage in tools --- clippy_lints/src/misc_early/mod.rs | 4 ++-- clippy_lints/src/trait_bounds.rs | 20 ++++++++++---------- clippy_lints/src/types/type_complexity.rs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index fedcfd11fdccc..4cba13a05c246 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -364,8 +364,8 @@ declare_lint_pass!(MiscEarlyLints => [ ]); impl EarlyLintPass for MiscEarlyLints { - fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { - for param in &gen.params { + fn check_generics(&mut self, cx: &EarlyContext<'_>, generics: &Generics) { + for param in &generics.params { builtin_type_shadow::check(cx, param); } } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c05cd9ed59345..07390d6f43029 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -102,9 +102,9 @@ impl TraitBounds { impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { - fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { - self.check_type_repetition(cx, gen); - check_trait_bound_duplication(cx, gen); + fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { + self.check_type_repetition(cx, generics); + check_trait_bound_duplication(cx, generics); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { @@ -238,7 +238,7 @@ impl TraitBounds { } #[allow(clippy::mutable_key_type)] - fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, cx: &'cx LateContext<'tcx>, @@ -258,12 +258,12 @@ impl TraitBounds { } impl Eq for SpanlessTy<'_, '_> {} - if gen.span.from_expansion() { + if generics.span.from_expansion() { return; } let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; - for bound in gen.predicates { + for bound in generics.predicates { if let WherePredicate::BoundPredicate(ref p) = bound && p.origin != PredicateOrigin::ImplTrait && p.bounds.len() as u64 <= self.max_trait_bounds @@ -301,8 +301,8 @@ impl TraitBounds { } } -fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if gen.span.from_expansion() { +fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_>) { + if generics.span.from_expansion() { return; } @@ -313,7 +313,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { // | // collects each of these where clauses into a set keyed by generic name and comparable trait // eg. (T, Clone) - let where_predicates = gen + let where_predicates = generics .predicates .iter() .filter_map(|pred| { @@ -342,7 +342,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { // | // compare trait bounds keyed by generic name and comparable trait to collected where // predicates eg. (T, Clone) - for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) { + for predicate in generics.predicates.iter().filter(|pred| !pred.in_where_clause()) { if let WherePredicate::BoundPredicate(bound_predicate) = predicate && bound_predicate.origin != PredicateOrigin::ImplTrait && !bound_predicate.span.from_expansion() diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 0aa50c99c1690..b6e765d7c3937 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -57,7 +57,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { bound .bound_generic_params .iter() - .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) + .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) }); if has_lifetime_parameters { // complex trait bounds like A<'a, 'b> From 3b390d416d06be695f2bef2f5ed85b15f5d2fef2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 15 Jul 2024 08:44:02 +0000 Subject: [PATCH 102/806] Bump clippy version --- Cargo.toml | 2 +- tests/compile-test.rs | 5 +- .../index_refutable_slice.fixed | 24 - .../index_refutable_slice.rs | 2 + .../index_refutable_slice.stderr | 2 +- tests/ui/crashes/ice-3717.fixed | 11 - tests/ui/crashes/ice-3717.rs | 2 + tests/ui/crashes/ice-3717.stderr | 2 +- tests/ui/derivable_impls.fixed | 295 --------- tests/ui/derivable_impls.rs | 2 + tests/ui/derivable_impls.stderr | 16 +- .../if_let_slice_binding.fixed | 177 ------ .../if_let_slice_binding.rs | 2 + .../if_let_slice_binding.stderr | 20 +- .../slice_indexing_in_macro.fixed | 29 - .../slice_indexing_in_macro.rs | 2 + .../slice_indexing_in_macro.stderr | 2 +- tests/ui/let_unit.fixed | 196 ------ tests/ui/let_unit.rs | 2 + tests/ui/let_unit.stderr | 8 +- tests/ui/manual_assert.edition2018.fixed | 76 --- tests/ui/manual_assert.edition2018.stderr | 20 +- tests/ui/manual_assert.edition2021.fixed | 76 --- tests/ui/manual_assert.edition2021.stderr | 20 +- tests/ui/manual_assert.rs | 2 + tests/ui/manual_async_fn.fixed | 115 ---- tests/ui/manual_async_fn.rs | 2 + tests/ui/manual_async_fn.stderr | 26 +- tests/ui/manual_split_once.fixed | 144 ----- tests/ui/manual_split_once.rs | 2 + tests/ui/manual_split_once.stderr | 38 +- tests/ui/match_same_arms2.fixed | 258 -------- tests/ui/match_same_arms2.rs | 3 + tests/ui/match_same_arms2.stderr | 30 +- tests/ui/significant_drop_tightening.fixed | 144 ----- tests/ui/significant_drop_tightening.rs | 2 + tests/ui/significant_drop_tightening.stderr | 8 +- tests/ui/unnecessary_iter_cloned.fixed | 201 ------ tests/ui/unnecessary_iter_cloned.rs | 2 + tests/ui/unnecessary_iter_cloned.stderr | 10 +- tests/ui/unnecessary_to_owned.fixed | 587 ------------------ tests/ui/unnecessary_to_owned.rs | 2 + tests/ui/unnecessary_to_owned.stderr | 186 +++--- 43 files changed, 225 insertions(+), 2530 deletions(-) delete mode 100644 tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed delete mode 100644 tests/ui/crashes/ice-3717.fixed delete mode 100644 tests/ui/derivable_impls.fixed delete mode 100644 tests/ui/index_refutable_slice/if_let_slice_binding.fixed delete mode 100644 tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed delete mode 100644 tests/ui/let_unit.fixed delete mode 100644 tests/ui/manual_assert.edition2018.fixed delete mode 100644 tests/ui/manual_assert.edition2021.fixed delete mode 100644 tests/ui/manual_async_fn.fixed delete mode 100644 tests/ui/manual_split_once.fixed delete mode 100644 tests/ui/match_same_arms2.fixed delete mode 100644 tests/ui/significant_drop_tightening.fixed delete mode 100644 tests/ui/unnecessary_iter_cloned.fixed delete mode 100644 tests/ui/unnecessary_to_owned.fixed diff --git a/Cargo.toml b/Cargo.toml index 4378849905514..5e52fd7e57ce8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.23" +ui_test = "0.24" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 333a2ab585757..e8c7cd1bc417e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -5,7 +5,7 @@ use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; +use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; use std::collections::BTreeMap; use std::env::{self, set_var, var_os}; @@ -122,7 +122,8 @@ fn base_config(test_dir: &str) -> (Config, Args) { out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; - config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo)).into(); + config.comment_defaults.base().exit_status = None.into(); + config.comment_defaults.base().require_annotations = None.into(); config .comment_defaults .base() diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed deleted file mode 100644 index 36540bf1dcf73..0000000000000 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ /dev/null @@ -1,24 +0,0 @@ -#![deny(clippy::index_refutable_slice)] - -fn below_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { - //~^ ERROR: binding can be a slice pattern - // This would usually not be linted but is included now due to the - // index limit in the config file - println!("{}", slice_7); - } -} - -fn above_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - // This will not be linted as 8 is above the limit - println!("{}", slice[8]); - } -} - -fn main() { - below_limit(); - above_limit(); -} diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd961..e64c8ff329001 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,5 +1,7 @@ #![deny(clippy::index_refutable_slice)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some(slice) = slice { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 0b989e5cf0647..3ea600c7d7b0c 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:7:17 | LL | if let Some(slice) = slice { | ^^^^^ diff --git a/tests/ui/crashes/ice-3717.fixed b/tests/ui/crashes/ice-3717.fixed deleted file mode 100644 index 3f54b326979c9..0000000000000 --- a/tests/ui/crashes/ice-3717.fixed +++ /dev/null @@ -1,11 +0,0 @@ -#![deny(clippy::implicit_hasher)] - -use std::collections::HashSet; - -fn main() {} - -pub fn ice_3717(_: &HashSet) { - //~^ ERROR: parameter of type `HashSet` should be generalized over different hashers - let _ = [0u8; 0]; - let _: HashSet = HashSet::default(); -} diff --git a/tests/ui/crashes/ice-3717.rs b/tests/ui/crashes/ice-3717.rs index 2890a9277c719..770f6cf448a63 100644 --- a/tests/ui/crashes/ice-3717.rs +++ b/tests/ui/crashes/ice-3717.rs @@ -1,5 +1,7 @@ #![deny(clippy::implicit_hasher)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::collections::HashSet; fn main() {} diff --git a/tests/ui/crashes/ice-3717.stderr b/tests/ui/crashes/ice-3717.stderr index 54b18a9641afc..01627ff5b94b9 100644 --- a/tests/ui/crashes/ice-3717.stderr +++ b/tests/ui/crashes/ice-3717.stderr @@ -1,5 +1,5 @@ error: parameter of type `HashSet` should be generalized over different hashers - --> tests/ui/crashes/ice-3717.rs:7:21 + --> tests/ui/crashes/ice-3717.rs:9:21 | LL | pub fn ice_3717(_: &HashSet) { | ^^^^^^^^^^^^^^ diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed deleted file mode 100644 index c85f384fd6eb9..0000000000000 --- a/tests/ui/derivable_impls.fixed +++ /dev/null @@ -1,295 +0,0 @@ -#![allow(dead_code)] - -use std::collections::HashMap; - -#[derive(Default)] -struct FooDefault<'a> { - a: bool, - b: i32, - c: u64, - d: Vec, - e: FooND1, - f: FooND2, - g: HashMap, - h: (i32, Vec), - i: [Vec; 3], - j: [i32; 5], - k: Option, - l: &'a [i32], -} - - -#[derive(Default)] -struct TupleDefault(bool, i32, u64); - - -struct FooND1 { - a: bool, -} - -impl std::default::Default for FooND1 { - fn default() -> Self { - Self { a: true } - } -} - -struct FooND2 { - a: i32, -} - -impl std::default::Default for FooND2 { - fn default() -> Self { - Self { a: 5 } - } -} - -struct FooNDNew { - a: bool, -} - -impl FooNDNew { - fn new() -> Self { - Self { a: true } - } -} - -impl Default for FooNDNew { - fn default() -> Self { - Self::new() - } -} - -struct FooNDVec(Vec); - -impl Default for FooNDVec { - fn default() -> Self { - Self(vec![5, 12]) - } -} - -#[derive(Default)] -struct StrDefault<'a>(&'a str); - - -#[derive(Default)] -struct AlreadyDerived(i32, bool); - -macro_rules! mac { - () => { - 0 - }; - ($e:expr) => { - struct X(u32); - impl Default for X { - fn default() -> Self { - Self($e) - } - } - }; -} - -mac!(0); - -#[derive(Default)] -struct Y(u32); - -struct RustIssue26925 { - a: Option, -} - -// We should watch out for cases where a manual impl is needed because a -// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925). -// For example, a struct with Option does not require T: Default, but a derive adds -// that type bound anyways. So until #26925 get fixed we should disable lint -// for the following case -impl Default for RustIssue26925 { - fn default() -> Self { - Self { a: None } - } -} - -struct SpecializedImpl { - a: A, - b: B, -} - -impl Default for SpecializedImpl { - fn default() -> Self { - Self { - a: T::default(), - b: T::default(), - } - } -} - -#[derive(Default)] -struct WithoutSelfCurly { - a: bool, -} - - -#[derive(Default)] -struct WithoutSelfParan(bool); - - -// https://github.com/rust-lang/rust-clippy/issues/7655 - -pub struct SpecializedImpl2 { - v: Vec, -} - -impl Default for SpecializedImpl2 { - fn default() -> Self { - Self { v: Vec::new() } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7654 - -pub struct Color { - pub r: u8, - pub g: u8, - pub b: u8, -} - -/// `#000000` -impl Default for Color { - fn default() -> Self { - Color { r: 0, g: 0, b: 0 } - } -} - -pub struct Color2 { - pub r: u8, - pub g: u8, - pub b: u8, -} - -impl Default for Color2 { - /// `#000000` - fn default() -> Self { - Self { r: 0, g: 0, b: 0 } - } -} - -#[derive(Default)] -pub struct RepeatDefault1 { - a: [i8; 32], -} - - -pub struct RepeatDefault2 { - a: [i8; 33], -} - -impl Default for RepeatDefault2 { - fn default() -> Self { - RepeatDefault2 { a: [0; 33] } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7753 - -pub enum IntOrString { - Int(i32), - String(String), -} - -impl Default for IntOrString { - fn default() -> Self { - IntOrString::Int(0) - } -} - -#[derive(Default)] -pub enum SimpleEnum { - Foo, - #[default] - Bar, -} - - -pub enum NonExhaustiveEnum { - Foo, - #[non_exhaustive] - Bar, -} - -impl Default for NonExhaustiveEnum { - fn default() -> Self { - NonExhaustiveEnum::Bar - } -} - -// https://github.com/rust-lang/rust-clippy/issues/10396 - -#[derive(Default)] -struct DefaultType; - -struct GenericType { - t: T, -} - -impl Default for GenericType { - fn default() -> Self { - Self { t: Default::default() } - } -} - -struct InnerGenericType { - t: T, -} - -impl Default for InnerGenericType { - fn default() -> Self { - Self { t: Default::default() } - } -} - -struct OtherGenericType { - inner: InnerGenericType, -} - -impl Default for OtherGenericType { - fn default() -> Self { - Self { - inner: Default::default(), - } - } -} - -mod issue10158 { - pub trait T {} - - #[derive(Default)] - pub struct S {} - impl T for S {} - - pub struct Outer { - pub inner: Box, - } - - impl Default for Outer { - fn default() -> Self { - Outer { - // Box::::default() adjusts to Box - inner: Box::::default(), - } - } - } -} - -mod issue11368 { - pub struct A { - a: u32, - } - - impl Default for A { - #[track_caller] - fn default() -> Self { - Self { a: 0 } - } - } -} - -fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 21d73ba8b7778..58f7771b6275b 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::collections::HashMap; struct FooDefault<'a> { diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index 0adb422373d86..d3adfa60e10f0 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:20:1 + --> tests/ui/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | fn default() -> Self { @@ -20,7 +20,7 @@ LL | struct FooDefault<'a> { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:41:1 + --> tests/ui/derivable_impls.rs:43:1 | LL | / impl std::default::Default for TupleDefault { LL | | fn default() -> Self { @@ -37,7 +37,7 @@ LL | struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:93:1 + --> tests/ui/derivable_impls.rs:95:1 | LL | / impl Default for StrDefault<'_> { LL | | fn default() -> Self { @@ -54,7 +54,7 @@ LL | struct StrDefault<'a>(&'a str); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:119:1 + --> tests/ui/derivable_impls.rs:121:1 | LL | / impl Default for Y { LL | | fn default() -> Self { @@ -71,7 +71,7 @@ LL | struct Y(u32); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:158:1 + --> tests/ui/derivable_impls.rs:160:1 | LL | / impl Default for WithoutSelfCurly { LL | | fn default() -> Self { @@ -88,7 +88,7 @@ LL | struct WithoutSelfCurly { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:166:1 + --> tests/ui/derivable_impls.rs:168:1 | LL | / impl Default for WithoutSelfParan { LL | | fn default() -> Self { @@ -105,7 +105,7 @@ LL | struct WithoutSelfParan(bool); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:216:1 + --> tests/ui/derivable_impls.rs:218:1 | LL | / impl Default for RepeatDefault1 { LL | | fn default() -> Self { @@ -122,7 +122,7 @@ LL | pub struct RepeatDefault1 { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:250:1 + --> tests/ui/derivable_impls.rs:252:1 | LL | / impl Default for SimpleEnum { LL | | fn default() -> Self { diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed deleted file mode 100644 index 13f0cbe9cc887..0000000000000 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ /dev/null @@ -1,177 +0,0 @@ -#![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args)] - -enum SomeEnum { - One(T), - Two(T), - Three(T), - Four(T), -} - -fn lintable_examples() { - // Try with reference - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Try with copy - let slice: Option<[u32; 3]> = Some([1, 2, 3]); - if let Some([slice_0, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Try with long slice and small indices - let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]); - if let Some([slice_0, _, slice_2, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_2); - println!("{}", slice_0); - } - - // Multiple bindings - let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]); - if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Two lintable slices in one if let - let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]); - let b_wrapped: Option<[u32; 2]> = Some([4, 6]); - if let (SomeEnum::Three([_, _, a_2, ..]), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - //~| ERROR: this binding can be a slice pattern to avoid indexing - println!("{} -> {}", a_2, b_1); - } - - // This requires the slice values to be borrowed as the slice values can only be - // borrowed and `String` doesn't implement copy - let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); - if let Some([_, ref slice_1, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{:?}", slice_1); - } - println!("{:?}", slice); - - // This should not suggest using the `ref` keyword as the scrutinee is already - // a reference - let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); - if let Some([slice_0, ..]) = &slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{:?}", slice_0); - } - println!("{:?}", slice); -} - -fn slice_index_above_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - - if let Some(slice) = slice { - // Would cause a panic, IDK - println!("{}", slice[7]); - } -} - -fn slice_is_used() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{:?}", slice.len()); - } - - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{:?}", slice.to_vec()); - } - - let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); - if let Some(slice) = opt { - if !slice.is_empty() { - println!("first: {}", slice[0]); - } - } -} - -/// The slice is used by an external function and should therefore not be linted -fn check_slice_as_arg() { - fn is_interesting(slice: &[T; 2]) -> bool { - !slice.is_empty() - } - - let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); - if let Some(slice) = &slice_wrapped { - if is_interesting(slice) { - println!("This is interesting {}", slice[0]); - } - } - println!("{:?}", slice_wrapped); -} - -fn check_slice_in_struct() { - #[derive(Debug)] - struct Wrapper<'a> { - inner: Option<&'a [String]>, - is_awesome: bool, - } - - impl<'a> Wrapper<'a> { - fn is_super_awesome(&self) -> bool { - self.is_awesome - } - } - - let inner = &[String::from("New"), String::from("World")]; - let wrap = Wrapper { - inner: Some(inner), - is_awesome: true, - }; - - // Test 1: Field access - if let Some([slice_0, ..]) = wrap.inner { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - if wrap.is_awesome { - println!("This is awesome! {}", slice_0); - } - } - - // Test 2: function access - if let Some([slice_0, ..]) = wrap.inner { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - if wrap.is_super_awesome() { - println!("This is super awesome! {}", slice_0); - } - } - println!("Complete wrap: {:?}", wrap); -} - -/// This would be a nice additional feature to have in the future, but adding it -/// now would make the PR too large. This is therefore only a test that we don't -/// lint cases we can't make a reasonable suggestion for -fn mutable_slice_index() { - // Mut access - let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]); - if let Some(ref mut slice) = slice { - slice[0] = String::from("Mr. Penguin"); - } - println!("Use after modification: {:?}", slice); - - // Mut access on reference - let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); - if let Some(slice) = &mut slice { - slice[0] = String::from("Lord Meow Meow"); - } - println!("Use after modification: {:?}", slice); -} - -/// The lint will ignore bindings with sub patterns as it would be hard -/// to build correct suggestions for these instances :) -fn binding_with_sub_pattern() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice @ [_, _, _]) = slice { - println!("{:?}", slice[2]); - } -} - -fn main() {} diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index d8d38c167fa57..5bbdabcaad19b 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,6 +1,8 @@ #![deny(clippy::index_refutable_slice)] #![allow(clippy::uninlined_format_args)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + enum SomeEnum { One(T), Two(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index 14e0f931f2401..8819cb0e28bfb 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:14:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:16:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:21:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:23:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -34,7 +34,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:28:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:30:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -50,7 +50,7 @@ LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:36:26 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:38:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ @@ -65,7 +65,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:29 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -80,7 +80,7 @@ LL | println!("{} -> {}", a_2, b[1]); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:38 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -95,7 +95,7 @@ LL | println!("{} -> {}", a[2], b_1); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:53:21 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:55:21 | LL | if let Some(ref slice) = slice { | ^^^^^ @@ -110,7 +110,7 @@ LL | println!("{:?}", slice_1); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:62:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:64:17 | LL | if let Some(slice) = &slice { | ^^^^^ @@ -125,7 +125,7 @@ LL | println!("{:?}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:132:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:134:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ @@ -140,7 +140,7 @@ LL | println!("This is awesome! {}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:140:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:142:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed b/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed deleted file mode 100644 index 72edc539f0431..0000000000000 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![deny(clippy::index_refutable_slice)] - -extern crate if_chain; -use if_chain::if_chain; - -macro_rules! if_let_slice_macro { - () => { - // This would normally be linted - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{}", slice[0]); - } - }; -} - -fn main() { - // Don't lint this - if_let_slice_macro!(); - - // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice; - //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice_0); - } - } -} diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs index 7b474ba423b9a..5d9fad4888999 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs @@ -1,5 +1,7 @@ #![deny(clippy::index_refutable_slice)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + extern crate if_chain; use if_chain::if_chain; diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr index caccd0f9295e1..69f0aaa977785 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:23:21 + --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:25:21 | LL | if let Some(slice) = slice; | ^^^^^ diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed deleted file mode 100644 index 3456e274f6a4d..0000000000000 --- a/tests/ui/let_unit.fixed +++ /dev/null @@ -1,196 +0,0 @@ -#![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] - -macro_rules! let_and_return { - ($n:expr) => {{ - let ret = $n; - }}; -} - -fn main() { - println!("x"); - let _y = 1; // this is fine - let _z = ((), 1); // this as well - if true { - // do not lint this, since () is explicit - let _a = (); - let () = dummy(); - let () = (); - () = dummy(); - () = (); - let _a: () = (); - let _a: () = dummy(); - } - - consume_units_with_for_loop(); // should be fine as well - - multiline_sugg(); - - let_and_return!(()) // should be fine -} - -fn dummy() {} - -// Related to issue #1964 -fn consume_units_with_for_loop() { - // `for_let_unit` lint should not be triggered by consuming them using for loop. - let v = vec![(), (), ()]; - let mut count = 0; - for _ in v { - count += 1; - } - assert_eq!(count, 3); - - // Same for consuming from some other Iterator. - let (tx, rx) = ::std::sync::mpsc::channel(); - tx.send(()).unwrap(); - drop(tx); - - count = 0; - for _ in rx.iter() { - count += 1; - } - assert_eq!(count, 1); -} - -fn multiline_sugg() { - let v: Vec = vec![2]; - - v - .into_iter() - .map(|i| i * 2) - .filter(|i| i % 2 == 0) - .map(|_| ()) - .next() - .unwrap(); -} - -#[derive(Copy, Clone)] -pub struct ContainsUnit(()); // should be fine - -fn _returns_generic() { - fn f() -> T { - unimplemented!() - } - fn f2(_: T) -> U { - unimplemented!() - } - fn f3(x: T) -> T { - x - } - fn f5(x: bool) -> Option { - x.then(|| T::default()) - } - - let _: () = f(); - let x: () = f(); - - let _: () = f2(0i32); - let x: () = f2(0i32); - - let _: () = f3(()); - let x: () = f3(()); - - fn f4(mut x: Vec) -> T { - x.pop().unwrap() - } - let _: () = f4(vec![()]); - let x: () = f4(vec![()]); - - let _: () = { - let x = 5; - f2(x) - }; - - let _: () = if true { f() } else { f2(0) }; - let x: () = if true { f() } else { f2(0) }; - - match Some(0) { - None => f2(1), - Some(0) => f(), - Some(1) => f2(3), - Some(_) => (), - }; - - let _: () = f5(true).unwrap(); - - #[allow(clippy::let_unit_value)] - { - let x = f(); - let y; - let z; - match 0 { - 0 => { - y = f(); - z = f(); - }, - 1 => { - println!("test"); - y = f(); - z = f3(()); - }, - _ => panic!(), - } - - let x1; - let x2; - if true { - x1 = f(); - x2 = x1; - } else { - x2 = f(); - x1 = x2; - } - - let opt; - match f5(true) { - Some(x) => opt = x, - None => panic!(), - }; - - #[warn(clippy::let_unit_value)] - { - let _: () = x; - let _: () = y; - let _: () = z; - let _: () = x1; - let _: () = x2; - let _: () = opt; - } - } - - let () = f(); -} - -fn attributes() { - fn f() {} - - #[allow(clippy::let_unit_value)] - let _ = f(); - #[expect(clippy::let_unit_value)] - let _ = f(); -} - -async fn issue10433() { - let _pending: () = std::future::pending().await; -} - -pub async fn issue11502(a: ()) {} - -pub fn issue12594() { - fn returns_unit() {} - - fn returns_result(res: T) -> Result { - Ok(res) - } - - fn actual_test() { - // create first a unit value'd value - returns_unit(); - returns_result(()).unwrap(); - returns_result(()).unwrap(); - // make sure we replace only the first variable - let res = 1; - returns_result(res).unwrap(); - } -} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index e2dafbcb77146..530103ffaf6ec 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,6 +1,8 @@ #![warn(clippy::let_unit_value)] #![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + macro_rules! let_and_return { ($n:expr) => {{ let ret = $n; diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 2f62c33c88730..6f149454af2d0 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -1,5 +1,5 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:13:5 | LL | let _x = println!("x"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` @@ -8,7 +8,7 @@ LL | let _x = println!("x"); = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` error: this let-binding has unit value - --> tests/ui/let_unit.rs:59:5 + --> tests/ui/let_unit.rs:61:5 | LL | / let _ = v LL | | .into_iter() @@ -31,7 +31,7 @@ LL + .unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:108:5 + --> tests/ui/let_unit.rs:110:5 | LL | / let x = match Some(0) { LL | | None => f2(1), @@ -52,7 +52,7 @@ LL + }; | error: this let-binding has unit value - --> tests/ui/let_unit.rs:189:9 + --> tests/ui/let_unit.rs:191:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed deleted file mode 100644 index 7583578080100..0000000000000 --- a/tests/ui/manual_assert.edition2018.fixed +++ /dev/null @@ -1,76 +0,0 @@ -//@revisions: edition2018 edition2021 -//@[edition2018] edition:2018 -//@[edition2021] edition:2021 - -#![warn(clippy::manual_assert)] -#![allow(dead_code, unused_doc_comments)] -#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] - -macro_rules! one { - () => { - 1 - }; -} - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - assert!(!a.is_empty(), "with expansion {}", one!()); - if a.is_empty() { - let _ = 0; - } else if a.len() == 1 { - panic!("panic6"); - } -} - -fn issue7730(a: u8) { - // Suggestion should preserve comment - // comment -/* this is a - multiline - comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); -} - -fn issue12505() { - struct Foo(T); - - impl Foo { - const BAR: () = assert!(!(N == 0), ); - } -} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 1eebe1bfe1771..004463720e26b 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:30:5 + --> tests/ui/manual_assert.rs:32:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); @@ -10,7 +10,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:33:5 + --> tests/ui/manual_assert.rs:35:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); @@ -18,7 +18,7 @@ LL | | } | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:50:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); @@ -26,7 +26,7 @@ LL | | } | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:53:5 + --> tests/ui/manual_assert.rs:55:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); @@ -34,7 +34,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:56:5 + --> tests/ui/manual_assert.rs:58:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); @@ -42,7 +42,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:59:5 + --> tests/ui/manual_assert.rs:61:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); @@ -50,7 +50,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); @@ -58,7 +58,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:65:5 + --> tests/ui/manual_assert.rs:67:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) @@ -66,7 +66,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:77:5 + --> tests/ui/manual_assert.rs:79:5 | LL | / if a > 2 { LL | | // comment @@ -83,7 +83,7 @@ LL | assert!(!(a > 2), "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:91:25 + --> tests/ui/manual_assert.rs:93:25 | LL | const BAR: () = if N == 0 { | _________________________^ diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed deleted file mode 100644 index 7583578080100..0000000000000 --- a/tests/ui/manual_assert.edition2021.fixed +++ /dev/null @@ -1,76 +0,0 @@ -//@revisions: edition2018 edition2021 -//@[edition2018] edition:2018 -//@[edition2021] edition:2021 - -#![warn(clippy::manual_assert)] -#![allow(dead_code, unused_doc_comments)] -#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] - -macro_rules! one { - () => { - 1 - }; -} - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - assert!(!a.is_empty(), "with expansion {}", one!()); - if a.is_empty() { - let _ = 0; - } else if a.len() == 1 { - panic!("panic6"); - } -} - -fn issue7730(a: u8) { - // Suggestion should preserve comment - // comment -/* this is a - multiline - comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); -} - -fn issue12505() { - struct Foo(T); - - impl Foo { - const BAR: () = assert!(!(N == 0), ); - } -} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 1eebe1bfe1771..004463720e26b 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:30:5 + --> tests/ui/manual_assert.rs:32:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); @@ -10,7 +10,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:33:5 + --> tests/ui/manual_assert.rs:35:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); @@ -18,7 +18,7 @@ LL | | } | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:50:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); @@ -26,7 +26,7 @@ LL | | } | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:53:5 + --> tests/ui/manual_assert.rs:55:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); @@ -34,7 +34,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:56:5 + --> tests/ui/manual_assert.rs:58:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); @@ -42,7 +42,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:59:5 + --> tests/ui/manual_assert.rs:61:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); @@ -50,7 +50,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); @@ -58,7 +58,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:65:5 + --> tests/ui/manual_assert.rs:67:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) @@ -66,7 +66,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:77:5 + --> tests/ui/manual_assert.rs:79:5 | LL | / if a > 2 { LL | | // comment @@ -83,7 +83,7 @@ LL | assert!(!(a > 2), "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:91:25 + --> tests/ui/manual_assert.rs:93:25 | LL | const BAR: () = if N == 0 { | _________________________^ diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 363bafdf05d0b..6337920a3eea1 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -2,6 +2,8 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 +//@no-rustfix: need to change the suggestion to a multipart suggestion + #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed deleted file mode 100644 index 18444090a4260..0000000000000 --- a/tests/ui/manual_async_fn.fixed +++ /dev/null @@ -1,115 +0,0 @@ -#![warn(clippy::manual_async_fn)] -#![allow(clippy::needless_pub_self, unused)] - -use std::future::Future; - -async fn fut() -> i32 { 42 } - -#[rustfmt::skip] -async fn fut2() -> i32 { 42 } - -#[rustfmt::skip] -async fn fut3() -> i32 { 42 } - -async fn empty_fut() {} - -#[rustfmt::skip] -async fn empty_fut2() {} - -#[rustfmt::skip] -async fn empty_fut3() {} - -async fn core_fut() -> i32 { 42 } - -// should be ignored -fn has_other_stmts() -> impl core::future::Future { - let _ = 42; - async move { 42 } -} - -// should be ignored -fn not_fut() -> i32 { - 42 -} - -// should be ignored -async fn already_async() -> impl Future { - async { 42 } -} - -struct S; -impl S { - async fn inh_fut() -> i32 { - // NOTE: this code is here just to check that the indentation is correct in the suggested fix - let a = 42; - let b = 21; - if a < b { - let c = 21; - let d = 42; - if c < d { - let _ = 42; - } - } - 42 - } - - // should be ignored - fn not_fut(&self) -> i32 { - 42 - } - - // should be ignored - fn has_other_stmts() -> impl core::future::Future { - let _ = 42; - async move { 42 } - } - - // should be ignored - async fn already_async(&self) -> impl Future { - async { 42 } - } -} - -// Tests related to lifetime capture - -async fn elided(_: &i32) -> i32 { 42 } - -// should be ignored -fn elided_not_bound(_: &i32) -> impl Future { - async { 42 } -} - -async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } - -// should be ignored -#[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { - async { 42 } -} - -// should be ignored -mod issue_5765 { - use std::future::Future; - - struct A; - impl A { - fn f(&self) -> impl Future { - async {} - } - } - - fn test() { - let _future = { - let a = A; - a.f() - }; - } -} - -pub async fn issue_10450() -> i32 { 42 } - -pub(crate) async fn issue_10450_2() -> i32 { 42 } - -pub(self) async fn issue_10450_3() -> i32 { 42 } - -fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index d42165bbe3d8c..6b8ac5033a92d 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -1,6 +1,8 @@ #![warn(clippy::manual_async_fn)] #![allow(clippy::needless_pub_self, unused)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::future::Future; fn fut() -> impl Future { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index c50af5a49883a..f88fc30b3b54e 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -1,5 +1,5 @@ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:6:1 + --> tests/ui/manual_async_fn.rs:8:1 | LL | fn fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn fut() -> impl Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:11:1 + --> tests/ui/manual_async_fn.rs:13:1 | LL | fn fut2() ->impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | fn fut2() ->impl Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:16:1 + --> tests/ui/manual_async_fn.rs:18:1 | LL | fn fut3()-> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | fn fut3()-> impl Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:20:1 + --> tests/ui/manual_async_fn.rs:22:1 | LL | fn empty_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | fn empty_fut() -> impl Future {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:25:1 + --> tests/ui/manual_async_fn.rs:27:1 | LL | fn empty_fut2() ->impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | fn empty_fut2() ->impl Future {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:30:1 + --> tests/ui/manual_async_fn.rs:32:1 | LL | fn empty_fut3()-> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | fn empty_fut3()-> impl Future {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:34:1 + --> tests/ui/manual_async_fn.rs:36:1 | LL | fn core_fut() -> impl core::future::Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | fn core_fut() -> impl core::future::Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:56:5 + --> tests/ui/manual_async_fn.rs:58:5 | LL | fn inh_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + } | error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:91:1 + --> tests/ui/manual_async_fn.rs:93:1 | LL | fn elided(_: &i32) -> impl Future + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,7 +148,7 @@ LL | fn elided(_: &i32) -> impl Future + '_ { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:100:1 + --> tests/ui/manual_async_fn.rs:102:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:129:1 + --> tests/ui/manual_async_fn.rs:131:1 | LL | pub fn issue_10450() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +178,7 @@ LL | pub fn issue_10450() -> impl Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:133:1 + --> tests/ui/manual_async_fn.rs:135:1 | LL | pub(crate) fn issue_10450_2() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL | pub(crate) fn issue_10450_2() -> impl Future { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:137:1 + --> tests/ui/manual_async_fn.rs:139:1 | LL | pub(self) fn issue_10450_3() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed deleted file mode 100644 index aaac6a048e1d0..0000000000000 --- a/tests/ui/manual_split_once.fixed +++ /dev/null @@ -1,144 +0,0 @@ -#![warn(clippy::manual_split_once)] -#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] - -extern crate itertools; - -#[allow(unused_imports)] -use itertools::Itertools; - -fn main() { - let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').unwrap().1; - let _ = "key=value".split_once('=').unwrap().1; - let (_, _) = "key=value".split_once('=').unwrap(); - - let s = String::from("key=value"); - let _ = s.split_once('=').unwrap().1; - - let s = Box::::from("key=value"); - let _ = s.split_once('=').unwrap().1; - - let s = &"key=value"; - let _ = s.split_once('=').unwrap().1; - - fn _f(s: &str) -> Option<&str> { - let _ = s.split_once('=')?.1; - let _ = s.split_once('=')?.1; - let _ = s.rsplit_once('=')?.0; - let _ = s.rsplit_once('=')?.0; - None - } - - // Don't lint, slices don't have `split_once` - let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); - - // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplit_once('=').unwrap().0; - let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); - let _ = s.rsplit_once('=').map(|x| x.0); -} - -fn indirect() -> Option<()> { - let (l, r) = "a.b.c".split_once('.').unwrap(); - - - - let (l, r) = "a.b.c".split_once('.')?; - - - - let (l, r) = "a.b.c".rsplit_once('.').unwrap(); - - - - let (l, r) = "a.b.c".rsplit_once('.')?; - - - - // could lint, currently doesn't - - let mut iter = "a.b.c".splitn(2, '.'); - let other = 1; - let l = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let mut mut_binding = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let tuple = (iter.next()?, iter.next()?); - - // should not lint - - let mut missing_unwrap = "a.b.c".splitn(2, '.'); - let l = missing_unwrap.next(); - let r = missing_unwrap.next(); - - let mut mixed_unrap = "a.b.c".splitn(2, '.'); - let unwrap = mixed_unrap.next().unwrap(); - let question_mark = mixed_unrap.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let same_name = iter.next()?; - let same_name = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let shadows_existing = "d"; - let shadows_existing = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let becomes_shadowed = iter.next()?; - let becomes_shadowed = "d"; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let l = iter.next()?; - let r = iter.next()?; - let third_usage = iter.next()?; - - let mut n_three = "a.b.c".splitn(3, '.'); - let l = n_three.next()?; - let r = n_three.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - { - let in_block = iter.next()?; - } - let r = iter.next()?; - - let mut lacks_binding = "a.b.c".splitn(2, '.'); - let _ = lacks_binding.next()?; - let r = lacks_binding.next()?; - - let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); - let l = iter.next()?; - let r = iter.next()?; - - let mut assigned = ""; - let mut iter = "a.b.c".splitn(2, '.'); - let l = iter.next()?; - assigned = iter.next()?; - - None -} - -#[clippy::msrv = "1.51"] -fn _msrv_1_51() { - // `str::split_once` was stabilized in 1.52. Do not lint this - let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - - let mut iter = "a.b.c".splitn(2, '.'); - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); -} - -#[clippy::msrv = "1.52"] -fn _msrv_1_52() { - let _ = "key=value".split_once('=').unwrap().1; - - let (a, b) = "a.b.c".split_once('.').unwrap(); - - -} diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 113e1737c97da..e13c827468b83 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -1,6 +1,8 @@ #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + extern crate itertools; #[allow(unused_imports)] diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index c5c9be3ac63df..566204ad8762e 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:11:13 + --> tests/ui/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` @@ -8,79 +8,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = help: to override `-D warnings` add `#[allow(clippy::manual_split_once)]` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:12:13 + --> tests/ui/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:13:18 + --> tests/ui/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:16:13 + --> tests/ui/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:19:13 + --> tests/ui/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:22:13 + --> tests/ui/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:25:17 + --> tests/ui/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:26:17 + --> tests/ui/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:27:17 + --> tests/ui/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:28:17 + --> tests/ui/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:36:13 + --> tests/ui/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:37:18 + --> tests/ui/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:38:13 + --> tests/ui/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:42:5 + --> tests/ui/manual_split_once.rs:44:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL - let r = iter.next().unwrap(); | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:46:5 + --> tests/ui/manual_split_once.rs:48:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL - let r = iter.next()?; | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:50:5 + --> tests/ui/manual_split_once.rs:52:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL - let l = iter.next().unwrap(); | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:54:5 + --> tests/ui/manual_split_once.rs:56:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -172,13 +172,13 @@ LL - let l = iter.next()?; | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:139:13 + --> tests/ui/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:141:5 + --> tests/ui/manual_split_once.rs:143:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_same_arms2.fixed b/tests/ui/match_same_arms2.fixed deleted file mode 100644 index 09e960ddd6a98..0000000000000 --- a/tests/ui/match_same_arms2.fixed +++ /dev/null @@ -1,258 +0,0 @@ -#![warn(clippy::match_same_arms)] -#![allow( - clippy::disallowed_names, - clippy::diverging_sub_expression, - clippy::uninlined_format_args, - clippy::match_single_binding, - clippy::match_like_matches_macro -)] -fn bar(_: T) {} -fn foo() -> bool { - unimplemented!() -} - -fn match_same_arms() { - let _ = match 42 { - _ => { - foo(); - let mut a = 42 + [23].len() as i32; - if true { - a += 7; - } - a = -31 - a; - a - }, - }; - //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm - - let _ = match 42 { - 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm - _ => true, - }; - - let _ = match Some(42) { - None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm - }; - - let _ = match Some(42) { - Some(foo) => 24, - None => 24, - }; - - let _ = match Some(42) { - Some(42) => 24, - Some(a) => 24, // bindings are different - None => 0, - }; - - let _ = match Some(42) { - Some(a) if a > 0 => 24, - Some(a) => 24, // one arm has a guard - None => 0, - }; - - match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm - _ => (), - } - - // No warning because guards are different - let _ = match Some(42) { - Some(a) if a == 42 => a, - Some(a) if a == 24 => a, - Some(_) => 24, - None => 0, - }; - - let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm - _ => 0, - }; - - match (Some(42), Some(42)) { - (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm - _ => (), - } - - let _ = match Some(()) { - Some(()) => 0.0, - None => -0.0, - }; - - match (Some(42), Some("")) { - (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), // bindings have different types - _ => (), - } - - let x: Result = Ok(3); - - // No warning because of the guard. - match x { - Ok(x) if x * x == 64 => println!("ok"), - Ok(_) => println!("ok"), - Err(_) => println!("err"), - } - - // This used to be a false positive; see issue #1996. - match x { - Ok(3) => println!("ok"), - Ok(x) if x * x == 64 => println!("ok 64"), - Ok(_) => println!("ok"), - Err(_) => println!("err"), - } - - match (x, Some(1i32)) { - (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm - _ => println!("err"), - } - - // No warning; different types for `x`. - match (x, Some(1.0f64)) { - (Ok(x), Some(_)) => println!("ok {}", x), - (Ok(_), Some(x)) => println!("ok {}", x), - _ => println!("err"), - } - - // False negative #2251. - match x { - Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm - Err(_) => { - unreachable!(); - }, - } - - // False positive #1390 - macro_rules! empty { - ($e:expr) => {}; - } - match 0 { - 0 => { - empty!(0); - }, - 1 => { - empty!(1); - }, - x => { - empty!(x); - }, - }; - - // still lint if the tokens are the same - match 0 { - 1 | 0 => { - empty!(0); - }, - x => { - empty!(x); - }, - } - //~^^^^^^^ ERROR: this match arm has an identical body to another arm - - match_expr_like_matches_macro_priority(); -} - -fn match_expr_like_matches_macro_priority() { - enum E { - A, - B, - C, - } - let x = E::A; - let _ans = match x { - E::A => false, - E::B => false, - _ => true, - }; -} - -fn main() { - let _ = match Some(0) { - Some(0) => 0, - Some(1) => 1, - #[cfg(feature = "foo")] - Some(2) => 2, - _ => 1, - }; - - enum Foo { - X(u32), - Y(u32), - Z(u32), - } - - // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. - let _ = match Foo::X(0) { - Foo::X(0) => 1, - Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) => 1, - _ => 0, - }; - - // Suggest moving `Foo::Z(_)` up. - let _ = match Foo::X(0) { - Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm - Foo::X(_) | Foo::Y(_) => 2, - _ => 0, - }; - - // Suggest moving `Foo::X(0)` down. - let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm - _ => 0, - }; - - // Don't lint. - let _ = match 0 { - -2 => 1, - -5..=50 => 2, - -150..=88 => 1, - _ => 3, - }; - - struct Bar { - x: u32, - y: u32, - z: u32, - } - - // Lint. - let _ = match None { - Some(Bar { y: 10, z: 0, .. }) => 2, - None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm - _ => 200, - }; - - let _ = match 0 { - 0 => todo!(), - 1 => todo!(), - 2 => core::convert::identity::(todo!()), - 3 => core::convert::identity::(todo!()), - _ => 5, - }; - - let _ = match 0 { - 1 | 0 => cfg!(not_enable), - _ => false, - }; -} - -// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 -mod with_lifetime { - enum MaybeStaticStr<'a> { - Static(&'static str), - Borrowed(&'a str), - } - - impl<'a> MaybeStaticStr<'a> { - fn get(&self) -> &'a str { - match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, - //~^ ERROR: this match arm has an identical body to another arm - } - } - } -} diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index cc7425135cc45..dedd02e78733f 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -6,6 +6,9 @@ clippy::match_single_binding, clippy::match_like_matches_macro )] + +//@no-rustfix: need to change the suggestion to a multipart suggestion + fn bar(_: T) {} fn foo() -> bool { unimplemented!() diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index a5d137c658b79..3a28b5afc2b8a 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:16:9 + --> tests/ui/match_same_arms2.rs:19:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | _ => { | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:25:9 + --> tests/ui/match_same_arms2.rs:28:9 | LL | / _ => { LL | | foo(); @@ -26,7 +26,7 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:39:9 + --> tests/ui/match_same_arms2.rs:42:9 | LL | 51 => foo(), | ^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL - 42 => foo(), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:45:9 + --> tests/ui/match_same_arms2.rs:48:9 | LL | None => 24, | ^^^^^^^^^^ @@ -58,7 +58,7 @@ LL - Some(_) => 24, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:67:9 + --> tests/ui/match_same_arms2.rs:70:9 | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL - (Some(a), None) => bar(a), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:81:9 + --> tests/ui/match_same_arms2.rs:84:9 | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL - (Some(a), None) if a == 42 => a, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:86:9 + --> tests/ui/match_same_arms2.rs:89:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL - (.., Some(a)) => bar(a), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:120:9 + --> tests/ui/match_same_arms2.rs:123:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL - (Ok(_), Some(x)) => println!("ok {}", x), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:136:9 + --> tests/ui/match_same_arms2.rs:139:9 | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +138,7 @@ LL - Ok(3) => println!("ok"), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:163:9 + --> tests/ui/match_same_arms2.rs:166:9 | LL | / 1 => { LL | | empty!(0); @@ -158,7 +158,7 @@ LL - }, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:214:9 + --> tests/ui/match_same_arms2.rs:217:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL - Foo::Z(_) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:224:9 + --> tests/ui/match_same_arms2.rs:227:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL - Foo::X(0) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:247:9 + --> tests/ui/match_same_arms2.rs:250:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +206,7 @@ LL - Some(Bar { x: 0, y: 5, .. }) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:261:9 + --> tests/ui/match_same_arms2.rs:264:9 | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL - 0 => cfg!(not_enable), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:277:17 + --> tests/ui/match_same_arms2.rs:280:17 | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/significant_drop_tightening.fixed b/tests/ui/significant_drop_tightening.fixed deleted file mode 100644 index ed05f6e0c8d35..0000000000000 --- a/tests/ui/significant_drop_tightening.fixed +++ /dev/null @@ -1,144 +0,0 @@ -#![warn(clippy::significant_drop_tightening)] - -use std::sync::Mutex; - -pub fn complex_return_triggers_the_lint() -> i32 { - fn foo() -> i32 { - 1 - } - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let _ = *lock; - let _ = *lock; - drop(lock); - foo() -} - -pub fn issue_10413() { - let mutex = Mutex::new(Some(1)); - let opt = Some(1); - if opt.is_some() { - let lock = mutex.lock().unwrap(); - let _ = *lock; - if opt.is_some() { - let _ = *lock; - } - } -} - -pub fn issue_11128() { - use std::mem::drop as unlock; - - struct Foo { - droppable: Option>, - mutex: Mutex>, - } - - impl Drop for Foo { - fn drop(&mut self) { - if let Some(droppable) = self.droppable.take() { - let lock = self.mutex.lock().unwrap(); - let idx_opt = lock.iter().copied().find(|el| Some(el) == droppable.first()); - if let Some(idx) = idx_opt { - let local_droppable = vec![lock.first().copied().unwrap_or_default()]; - unlock(lock); - drop(local_droppable); - } - } - } - } -} - -pub fn issue_11160() -> bool { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - true -} - -pub fn issue_11189() { - struct Number { - pub value: u32, - } - - fn do_something() -> Result<(), ()> { - let number = Mutex::new(Number { value: 1 }); - let number2 = Mutex::new(Number { value: 2 }); - let number3 = Mutex::new(Number { value: 3 }); - let mut lock = number.lock().unwrap(); - let mut lock2 = number2.lock().unwrap(); - let mut lock3 = number3.lock().unwrap(); - lock.value += 1; - lock2.value += 1; - lock3.value += 1; - drop((lock, lock2, lock3)); - Ok(()) - } -} - -pub fn path_return_can_be_ignored() -> i32 { - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let rslt = *lock; - let _ = *lock; - rslt -} - -pub fn post_bindings_can_be_ignored() { - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let rslt = *lock; - let another = rslt; - let _ = another; -} - -pub fn unnecessary_contention_with_multiple_owned_results() { - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - let _ = lock.is_positive(); - } - - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let rslt0 = lock.abs(); - let rslt1 = lock.is_positive(); - drop(lock); - do_heavy_computation_that_takes_time((rslt0, rslt1)); - } -} - -pub fn unnecessary_contention_with_single_owned_results() { - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - } - { - let mutex = Mutex::new(vec![1i32]); - let mut lock = mutex.lock().unwrap(); - lock.clear(); - } - - { - let mutex = Mutex::new(1i32); - - let rslt0 = mutex.lock().unwrap().abs(); - - do_heavy_computation_that_takes_time(rslt0); - } - { - let mutex = Mutex::new(vec![1i32]); - - mutex.lock().unwrap().clear(); - - do_heavy_computation_that_takes_time(()); - } -} - -// Marker used for illustration purposes. -pub fn do_heavy_computation_that_takes_time(_: T) {} - -fn main() {} diff --git a/tests/ui/significant_drop_tightening.rs b/tests/ui/significant_drop_tightening.rs index e5f17278f0f6a..77538167548ec 100644 --- a/tests/ui/significant_drop_tightening.rs +++ b/tests/ui/significant_drop_tightening.rs @@ -1,5 +1,7 @@ #![warn(clippy::significant_drop_tightening)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::sync::Mutex; pub fn complex_return_triggers_the_lint() -> i32 { diff --git a/tests/ui/significant_drop_tightening.stderr b/tests/ui/significant_drop_tightening.stderr index 5fc66279f00e7..2d7da4f394d39 100644 --- a/tests/ui/significant_drop_tightening.stderr +++ b/tests/ui/significant_drop_tightening.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:10:9 + --> tests/ui/significant_drop_tightening.rs:12:9 | LL | pub fn complex_return_triggers_the_lint() -> i32 { | __________________________________________________- @@ -24,7 +24,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:104:13 + --> tests/ui/significant_drop_tightening.rs:106:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -44,7 +44,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:125:13 + --> tests/ui/significant_drop_tightening.rs:127:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -67,7 +67,7 @@ LL - let rslt0 = lock.abs(); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:131:17 + --> tests/ui/significant_drop_tightening.rs:133:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); diff --git a/tests/ui/unnecessary_iter_cloned.fixed b/tests/ui/unnecessary_iter_cloned.fixed deleted file mode 100644 index dc5e163ff04e4..0000000000000 --- a/tests/ui/unnecessary_iter_cloned.fixed +++ /dev/null @@ -1,201 +0,0 @@ -#![allow(unused_assignments)] -#![warn(clippy::unnecessary_to_owned)] - -#[allow(dead_code)] -#[derive(Clone, Copy)] -enum FileType { - Account, - PrivateKey, - Certificate, -} - -fn main() { - let path = std::path::Path::new("x"); - - let _ = check_files(&[(FileType::Account, path)]); - let _ = check_files_vec(vec![(FileType::Account, path)]); - - // negative tests - let _ = check_files_ref(&[(FileType::Account, path)]); - let _ = check_files_mut(&[(FileType::Account, path)]); - let _ = check_files_ref_mut(&[(FileType::Account, path)]); - let _ = check_files_self_and_arg(&[(FileType::Account, path)]); - let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); - - check_mut_iteratee_and_modify_inner_variable(); -} - -// `check_files` and its variants are based on: -// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 -fn check_files(files: &[(FileType, &std::path::Path)]) -> bool { - for (t, path) in files { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool { - for (t, path) in files.iter() { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool { - for (ref t, path) in files.iter().copied() { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -#[allow(unused_assignments)] -fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool { - for (mut t, path) in files.iter().copied() { - t = FileType::PrivateKey; - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool { - for (ref mut t, path) in files.iter().copied() { - *t = FileType::PrivateKey; - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool { - for (t, path) in files.iter().copied() { - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.join(path).is_file() || !other.is_file() { - return false; - } - } - true -} - -#[allow(unused_assignments)] -fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { - for (mut t, path) in files.iter().cloned() { - t = FileType::PrivateKey; - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn get_file_path(_file_type: &FileType) -> Result { - Ok(std::path::PathBuf::new()) -} - -// Issue 12098 -// https://github.com/rust-lang/rust-clippy/issues/12098 -// no message emits -fn check_mut_iteratee_and_modify_inner_variable() { - struct Test { - list: Vec, - mut_this: bool, - } - - impl Test { - fn list(&self) -> &[String] { - &self.list - } - } - - let mut test = Test { - list: vec![String::from("foo"), String::from("bar")], - mut_this: false, - }; - - for _item in test.list().to_vec() { - println!("{}", _item); - - test.mut_this = true; - { - test.mut_this = true; - } - } -} - -mod issue_12821 { - fn foo() { - let v: Vec<_> = "hello".chars().collect(); - for c in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - println!("{c}"); // should not suggest to remove `&` - } - } - - fn bar() { - let v: Vec<_> = "hello".chars().collect(); - for c in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - let ref_c = c; //~ HELP: remove any references to the binding - println!("{ref_c}"); - } - } - - fn baz() { - let v: Vec<_> = "hello".chars().enumerate().collect(); - for (i, c) in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - let ref_c = c; //~ HELP: remove any references to the binding - let ref_i = i; - println!("{i} {ref_c}"); // should not suggest to remove `&` from `i` - } - } -} diff --git a/tests/ui/unnecessary_iter_cloned.rs b/tests/ui/unnecessary_iter_cloned.rs index 8f797ac717fb6..331b7b25271ba 100644 --- a/tests/ui/unnecessary_iter_cloned.rs +++ b/tests/ui/unnecessary_iter_cloned.rs @@ -1,6 +1,8 @@ #![allow(unused_assignments)] #![warn(clippy::unnecessary_to_owned)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + #[allow(dead_code)] #[derive(Clone, Copy)] enum FileType { diff --git a/tests/ui/unnecessary_iter_cloned.stderr b/tests/ui/unnecessary_iter_cloned.stderr index 0bdb37a521fca..e3592e3cbbd82 100644 --- a/tests/ui/unnecessary_iter_cloned.stderr +++ b/tests/ui/unnecessary_iter_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:31:22 + --> tests/ui/unnecessary_iter_cloned.rs:33:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:46:22 + --> tests/ui/unnecessary_iter_cloned.rs:48:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -33,13 +33,13 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:177:18 + --> tests/ui/unnecessary_iter_cloned.rs:179:18 | LL | for c in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ help: use: `v.iter()` error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:185:18 + --> tests/ui/unnecessary_iter_cloned.rs:187:18 | LL | for c in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL + let ref_c = c; | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:194:23 + --> tests/ui/unnecessary_iter_cloned.rs:196:23 | LL | for (i, c) in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed deleted file mode 100644 index fdcac8fb08dcf..0000000000000 --- a/tests/ui/unnecessary_to_owned.fixed +++ /dev/null @@ -1,587 +0,0 @@ -#![allow( - clippy::needless_borrow, - clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, - clippy::needless_lifetimes -)] -#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] - -use std::borrow::Cow; -use std::ffi::{CStr, CString, OsStr, OsString}; -use std::ops::Deref; - -#[derive(Clone)] -struct X(String); - -impl Deref for X { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl AsRef for X { - fn as_ref(&self) -> &str { - self.0.as_str() - } -} - -#[allow(clippy::to_string_trait_impl)] -impl ToString for X { - fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl X { - fn join(&self, other: impl AsRef) -> Self { - let mut s = self.0.clone(); - s.push_str(other.as_ref()); - Self(s) - } -} - -#[allow(dead_code)] -#[derive(Clone)] -enum FileType { - Account, - PrivateKey, - Certificate, -} - -fn main() { - let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); - let os_str = OsStr::new("x"); - let path = std::path::Path::new("x"); - let s = "x"; - let array = ["x"]; - let array_ref = &["x"]; - let slice = &["x"][..]; - let x = X(String::from("x")); - let x_ref = &x; - - require_c_str(&Cow::from(c_str)); - require_c_str(c_str); - - require_os_str(os_str); - require_os_str(&Cow::from(os_str)); - require_os_str(os_str); - - require_path(path); - require_path(&Cow::from(path)); - require_path(path); - - require_str(s); - require_str(&Cow::from(s)); - require_str(s); - require_str(x_ref.as_ref()); - - require_slice(slice); - require_slice(&Cow::from(slice)); - require_slice(array.as_ref()); - require_slice(array_ref.as_ref()); - require_slice(slice); - require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. - - require_x(&Cow::::Owned(x.clone())); - require_x(&x_ref.to_owned()); // No longer flagged because of #8759. - - require_deref_c_str(c_str); - require_deref_os_str(os_str); - require_deref_path(path); - require_deref_str(s); - require_deref_slice(slice); - - require_impl_deref_c_str(c_str); - require_impl_deref_os_str(os_str); - require_impl_deref_path(path); - require_impl_deref_str(s); - require_impl_deref_slice(slice); - - require_deref_str_slice(s, slice); - require_deref_slice_str(slice, s); - - require_as_ref_c_str(c_str); - require_as_ref_os_str(os_str); - require_as_ref_path(path); - require_as_ref_str(s); - require_as_ref_str(&x); - require_as_ref_slice(array); - require_as_ref_slice(array_ref); - require_as_ref_slice(slice); - - require_impl_as_ref_c_str(c_str); - require_impl_as_ref_os_str(os_str); - require_impl_as_ref_path(path); - require_impl_as_ref_str(s); - require_impl_as_ref_str(&x); - require_impl_as_ref_slice(array); - require_impl_as_ref_slice(array_ref); - require_impl_as_ref_slice(slice); - - require_as_ref_str_slice(s, array); - require_as_ref_str_slice(s, array_ref); - require_as_ref_str_slice(s, slice); - require_as_ref_slice_str(array, s); - require_as_ref_slice_str(array_ref, s); - require_as_ref_slice_str(slice, s); - - let _ = x.join(x_ref); - - let _ = slice.iter().copied(); - let _ = slice.iter().copied(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - - let _ = slice.iter().copied(); - let _ = slice.iter().copied(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - - let _ = check_files(&[FileType::Account]); - - // negative tests - require_string(&s.to_string()); - require_string(&Cow::from(s).into_owned()); - require_string(&s.to_owned()); - require_string(&x_ref.to_string()); - - // `X` isn't copy. - require_slice(&x.to_owned()); - require_deref_slice(x.to_owned()); - - // The following should be flagged by `redundant_clone`, but not by this lint. - require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap()); - require_os_str(&OsString::from("x")); - require_path(&std::path::PathBuf::from("x")); - require_str(&String::from("x")); - require_slice(&[String::from("x")]); - - let slice = [0u8; 1024]; - let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); - let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); - let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); - // Expression is of type `&String`, can't suggest `str::from_utf8` here - let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap(); - macro_rules! arg_from_macro { - () => { - b"foo".to_vec() - }; - } - macro_rules! string_from_utf8_from_macro { - () => { - &String::from_utf8(b"foo".to_vec()).unwrap() - }; - } - let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap(); - let _ref_str: &str = string_from_utf8_from_macro!(); -} - -fn require_c_str(_: &CStr) {} -fn require_os_str(_: &OsStr) {} -fn require_path(_: &std::path::Path) {} -fn require_str(_: &str) {} -fn require_slice(_: &[T]) {} -fn require_x(_: &X) {} - -fn require_deref_c_str>(_: T) {} -fn require_deref_os_str>(_: T) {} -fn require_deref_path>(_: T) {} -fn require_deref_str>(_: T) {} -fn require_deref_slice>(_: U) {} - -fn require_impl_deref_c_str(_: impl Deref) {} -fn require_impl_deref_os_str(_: impl Deref) {} -fn require_impl_deref_path(_: impl Deref) {} -fn require_impl_deref_str(_: impl Deref) {} -fn require_impl_deref_slice(_: impl Deref) {} - -fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} -fn require_deref_slice_str, V: Deref>(_: U, _: V) {} - -fn require_as_ref_c_str>(_: T) {} -fn require_as_ref_os_str>(_: T) {} -fn require_as_ref_path>(_: T) {} -fn require_as_ref_str>(_: T) {} -fn require_as_ref_slice>(_: U) {} - -fn require_impl_as_ref_c_str(_: impl AsRef) {} -fn require_impl_as_ref_os_str(_: impl AsRef) {} -fn require_impl_as_ref_path(_: impl AsRef) {} -fn require_impl_as_ref_str(_: impl AsRef) {} -fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} - -fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} -fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} - -// `check_files` is based on: -// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 -fn check_files(file_types: &[FileType]) -> bool { - for t in file_types { - let path = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() { - return false; - } - } - true -} - -fn get_file_path(_file_type: &FileType) -> Result { - Ok(std::path::PathBuf::new()) -} - -fn require_string(_: &String) {} - -#[clippy::msrv = "1.35"] -fn _msrv_1_35() { - // `copied` was stabilized in 1.36, so clippy should use `cloned`. - let _ = &["x"][..].iter().cloned(); -} - -#[clippy::msrv = "1.36"] -fn _msrv_1_36() { - let _ = &["x"][..].iter().copied(); -} - -// https://github.com/rust-lang/rust-clippy/issues/8507 -mod issue_8507 { - #![allow(dead_code)] - - struct Opaque

(P); - - pub trait Abstracted {} - - impl

Abstracted for Opaque

{} - - fn build

(p: P) -> Opaque

- where - P: AsRef, - { - Opaque(p) - } - - // Should not lint. - fn test_str(s: &str) -> Box { - Box::new(build(s.to_string())) - } - - // Should not lint. - fn test_x(x: super::X) -> Box { - Box::new(build(x)) - } - - #[derive(Clone, Copy)] - struct Y(&'static str); - - impl AsRef for Y { - fn as_ref(&self) -> &str { - self.0 - } - } - - #[allow(clippy::to_string_trait_impl)] - impl ToString for Y { - fn to_string(&self) -> String { - self.0.to_string() - } - } - - // Should lint because Y is copy. - fn test_y(y: Y) -> Box { - Box::new(build(y)) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/8759 -mod issue_8759 { - #![allow(dead_code)] - - #[derive(Default)] - struct View {} - - impl std::borrow::ToOwned for View { - type Owned = View; - fn to_owned(&self) -> Self::Owned { - View {} - } - } - - #[derive(Default)] - struct RenderWindow { - default_view: View, - } - - impl RenderWindow { - fn default_view(&self) -> &View { - &self.default_view - } - fn set_view(&mut self, _view: &View) {} - } - - fn main() { - let mut rw = RenderWindow::default(); - rw.set_view(&rw.default_view().to_owned()); - } -} - -mod issue_8759_variant { - #![allow(dead_code)] - - #[derive(Clone, Default)] - struct View {} - - #[derive(Default)] - struct RenderWindow { - default_view: View, - } - - impl RenderWindow { - fn default_view(&self) -> &View { - &self.default_view - } - fn set_view(&mut self, _view: &View) {} - } - - fn main() { - let mut rw = RenderWindow::default(); - rw.set_view(&rw.default_view().to_owned()); - } -} - -mod issue_9317 { - #![allow(dead_code)] - - struct Bytes {} - - #[allow(clippy::to_string_trait_impl)] - impl ToString for Bytes { - fn to_string(&self) -> String { - "123".to_string() - } - } - - impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - &[1, 2, 3] - } - } - - fn consume>(c: C) { - let _ = c; - } - - pub fn main() { - let b = Bytes {}; - // Should not lint. - consume(b.to_string()); - } -} - -mod issue_9351 { - #![allow(dead_code)] - - use std::ops::Deref; - use std::path::{Path, PathBuf}; - - fn require_deref_path>(x: T) -> T { - x - } - - fn generic_arg_used_elsewhere>(_x: T, _y: T) {} - - fn id>(x: T) -> T { - x - } - - fn predicates_are_satisfied(_x: impl std::fmt::Write) {} - - // Should lint - fn single_return() -> impl AsRef { - id("abc") - } - - // Should not lint - fn multiple_returns(b: bool) -> impl AsRef { - if b { - return String::new(); - } - - id("abc".to_string()) - } - - struct S1(String); - - // Should not lint - fn fields1() -> S1 { - S1(id("abc".to_string())) - } - - struct S2 { - s: String, - } - - // Should not lint - fn fields2() { - let mut s = S2 { s: "abc".into() }; - s.s = id("abc".to_string()); - } - - pub fn main() { - let path = std::path::Path::new("x"); - let path_buf = path.to_owned(); - - // Should not lint. - let _x: PathBuf = require_deref_path(path.to_owned()); - generic_arg_used_elsewhere(path.to_owned(), path_buf); - predicates_are_satisfied(id("abc".to_string())); - } -} - -mod issue_9504 { - #![allow(dead_code)] - - async fn foo>(_: S) {} - async fn bar() { - foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; - } -} - -mod issue_9771a { - #![allow(dead_code)] - - use std::marker::PhantomData; - - pub struct Key, V: ?Sized>(K, PhantomData); - - impl, V: ?Sized> Key { - pub fn new(key: K) -> Key { - Key(key, PhantomData) - } - } - - pub fn pkh(pkh: &[u8]) -> Key, String> { - Key::new([b"pkh-", pkh].concat().to_vec()) - } -} - -mod issue_9771b { - #![allow(dead_code)] - - pub struct Key>(K); - - pub fn from(c: &[u8]) -> Key> { - let v = [c].concat(); - Key(v.to_vec()) - } -} - -// This is a watered down version of the code in: https://github.com/oxigraph/rio -// The ICE is triggered by the call to `to_owned` on this line: -// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 -mod issue_10021 { - #![allow(unused)] - - pub struct Iri(T); - - impl> Iri { - pub fn parse(iri: T) -> Result { - unimplemented!() - } - } - - pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { - let base_iri = Iri::parse(url.to_owned())?; - Ok(()) - } -} - -mod issue_10033 { - #![allow(dead_code)] - use std::fmt::Display; - use std::ops::Deref; - - fn _main() { - let f = Foo; - - // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does - // deref to `str`) - foo(&f.to_string()); - } - - fn foo(s: &str) { - println!("{}", s); - } - - struct Foo; - - impl Deref for Foo { - type Target = str; - - fn deref(&self) -> &Self::Target { - "str" - } - } - - impl Display for Foo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Foo") - } - } -} - -mod issue_11952 { - use core::future::{Future, IntoFuture}; - - fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future> { - async move { - let _y = y; - Ok(()) - } - } - - fn bar() { - IntoFuture::into_future(foo([], &0)); - } -} - -fn borrow_checks() { - use std::borrow::Borrow; - use std::collections::HashSet; - - fn inner(a: &[&str]) { - let mut s = HashSet::from([vec!["a"]]); - s.remove(a); //~ ERROR: unnecessary use of `to_vec` - } - - let mut s = HashSet::from(["a".to_string()]); - s.remove("b"); //~ ERROR: unnecessary use of `to_owned` - s.remove("b"); //~ ERROR: unnecessary use of `to_string` - // Should not warn. - s.remove("b"); - - let mut s = HashSet::from([vec!["a"]]); - s.remove(["b"].as_slice()); //~ ERROR: unnecessary use of `to_vec` - s.remove((&["b"]).as_slice()); //~ ERROR: unnecessary use of `to_vec` - - // Should not warn. - s.remove(&["b"].to_vec().clone()); - s.remove(["a"].as_slice()); - - trait SetExt { - fn foo>(&self, _: &String); - } - - impl SetExt for HashSet { - fn foo>(&self, _: &String) {} - } - - // Should not lint! - HashSet::::new().foo::<&str>(&"".to_owned()); - HashSet::::new().get(&1.to_string()); -} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 10a9727a9a798..da0c761f795b3 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -7,6 +7,8 @@ )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 511b4ae119f8a..7ab1f667d9b58 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:155:64 + --> tests/ui/unnecessary_to_owned.rs:157:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:155:20 + --> tests/ui/unnecessary_to_owned.rs:157:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:156:40 + --> tests/ui/unnecessary_to_owned.rs:158:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:156:21 + --> tests/ui/unnecessary_to_owned.rs:158:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:157:48 + --> tests/ui/unnecessary_to_owned.rs:159:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:157:19 + --> tests/ui/unnecessary_to_owned.rs:159:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:158:35 + --> tests/ui/unnecessary_to_owned.rs:160:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:158:18 + --> tests/ui/unnecessary_to_owned.rs:160:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:159:39 + --> tests/ui/unnecessary_to_owned.rs:161:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:159:20 + --> tests/ui/unnecessary_to_owned.rs:161:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:64:36 + --> tests/ui/unnecessary_to_owned.rs:66:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:65:19 + --> tests/ui/unnecessary_to_owned.rs:67:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:67:20 + --> tests/ui/unnecessary_to_owned.rs:69:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:68:38 + --> tests/ui/unnecessary_to_owned.rs:70:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:69:20 + --> tests/ui/unnecessary_to_owned.rs:71:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:71:18 + --> tests/ui/unnecessary_to_owned.rs:73:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:72:34 + --> tests/ui/unnecessary_to_owned.rs:74:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:73:18 + --> tests/ui/unnecessary_to_owned.rs:75:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:75:17 + --> tests/ui/unnecessary_to_owned.rs:77:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:76:30 + --> tests/ui/unnecessary_to_owned.rs:78:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:77:17 + --> tests/ui/unnecessary_to_owned.rs:79:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:78:17 + --> tests/ui/unnecessary_to_owned.rs:80:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:80:19 + --> tests/ui/unnecessary_to_owned.rs:82:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:81:36 + --> tests/ui/unnecessary_to_owned.rs:83:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:82:19 + --> tests/ui/unnecessary_to_owned.rs:84:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:83:19 + --> tests/ui/unnecessary_to_owned.rs:85:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:84:19 + --> tests/ui/unnecessary_to_owned.rs:86:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:87:42 + --> tests/ui/unnecessary_to_owned.rs:89:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:90:25 + --> tests/ui/unnecessary_to_owned.rs:92:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:91:26 + --> tests/ui/unnecessary_to_owned.rs:93:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:92:24 + --> tests/ui/unnecessary_to_owned.rs:94:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:93:23 + --> tests/ui/unnecessary_to_owned.rs:95:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:94:25 + --> tests/ui/unnecessary_to_owned.rs:96:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:96:30 + --> tests/ui/unnecessary_to_owned.rs:98:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:97:31 + --> tests/ui/unnecessary_to_owned.rs:99:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:98:29 + --> tests/ui/unnecessary_to_owned.rs:100:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:28 + --> tests/ui/unnecessary_to_owned.rs:101:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:100:30 + --> tests/ui/unnecessary_to_owned.rs:102:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:102:29 + --> tests/ui/unnecessary_to_owned.rs:104:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:102:43 + --> tests/ui/unnecessary_to_owned.rs:104:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:103:29 + --> tests/ui/unnecessary_to_owned.rs:105:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:103:47 + --> tests/ui/unnecessary_to_owned.rs:105:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:105:26 + --> tests/ui/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:106:27 + --> tests/ui/unnecessary_to_owned.rs:108:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:107:25 + --> tests/ui/unnecessary_to_owned.rs:109:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:108:24 + --> tests/ui/unnecessary_to_owned.rs:110:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:24 + --> tests/ui/unnecessary_to_owned.rs:111:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:110:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:26 + --> tests/ui/unnecessary_to_owned.rs:113:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:112:26 + --> tests/ui/unnecessary_to_owned.rs:114:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:114:31 + --> tests/ui/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:115:32 + --> tests/ui/unnecessary_to_owned.rs:117:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:116:30 + --> tests/ui/unnecessary_to_owned.rs:118:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:29 + --> tests/ui/unnecessary_to_owned.rs:119:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:118:29 + --> tests/ui/unnecessary_to_owned.rs:120:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:119:31 + --> tests/ui/unnecessary_to_owned.rs:121:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:31 + --> tests/ui/unnecessary_to_owned.rs:122:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:121:31 + --> tests/ui/unnecessary_to_owned.rs:123:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:30 + --> tests/ui/unnecessary_to_owned.rs:125:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:44 + --> tests/ui/unnecessary_to_owned.rs:125:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:30 + --> tests/ui/unnecessary_to_owned.rs:126:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:44 + --> tests/ui/unnecessary_to_owned.rs:126:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:30 + --> tests/ui/unnecessary_to_owned.rs:127:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:44 + --> tests/ui/unnecessary_to_owned.rs:127:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:30 + --> tests/ui/unnecessary_to_owned.rs:128:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:48 + --> tests/ui/unnecessary_to_owned.rs:128:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:30 + --> tests/ui/unnecessary_to_owned.rs:129:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:52 + --> tests/ui/unnecessary_to_owned.rs:129:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:130:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:48 + --> tests/ui/unnecessary_to_owned.rs:130:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:130:20 + --> tests/ui/unnecessary_to_owned.rs:132:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:132:13 + --> tests/ui/unnecessary_to_owned.rs:134:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:133:13 + --> tests/ui/unnecessary_to_owned.rs:135:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:134:13 + --> tests/ui/unnecessary_to_owned.rs:136:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:135:13 + --> tests/ui/unnecessary_to_owned.rs:137:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:137:13 + --> tests/ui/unnecessary_to_owned.rs:139:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:138:13 + --> tests/ui/unnecessary_to_owned.rs:140:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:139:13 + --> tests/ui/unnecessary_to_owned.rs:141:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:13 + --> tests/ui/unnecessary_to_owned.rs:142:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:162:26 + --> tests/ui/unnecessary_to_owned.rs:164:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:163:26 + --> tests/ui/unnecessary_to_owned.rs:165:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +502,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:164:26 + --> tests/ui/unnecessary_to_owned.rs:166:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +514,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:221:14 + --> tests/ui/unnecessary_to_owned.rs:223:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -530,61 +530,61 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:244:14 + --> tests/ui/unnecessary_to_owned.rs:246:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:249:14 + --> tests/ui/unnecessary_to_owned.rs:251:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:297:24 + --> tests/ui/unnecessary_to_owned.rs:299:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:406:12 + --> tests/ui/unnecessary_to_owned.rs:408:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:549:37 + --> tests/ui/unnecessary_to_owned.rs:551:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:559:18 + --> tests/ui/unnecessary_to_owned.rs:561:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:563:14 + --> tests/ui/unnecessary_to_owned.rs:565:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:564:14 + --> tests/ui/unnecessary_to_owned.rs:566:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:569:14 + --> tests/ui/unnecessary_to_owned.rs:571:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:570:14 + --> tests/ui/unnecessary_to_owned.rs:572:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` From d95b2f3f4bb5839b508b86a751c09cb7e0d59cda Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jul 2024 11:07:04 +0200 Subject: [PATCH 103/806] Fix stable iteration ordering for `Map` usages --- .../crates/hir-def/src/item_scope.rs | 15 +++++----- .../crates/hir-def/src/nameres.rs | 10 +++---- .../hir-def/src/nameres/tests/macros.rs | 1 + .../crates/hir-def/src/resolver.rs | 9 ++++-- .../ide-assists/src/handlers/auto_import.rs | 28 ++++++++++++++++--- .../src/completions/format_string.rs | 2 +- .../ide-completion/src/render/function.rs | 2 +- .../ide-completion/src/tests/flyimport.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 10 +++---- .../rust-analyzer/crates/intern/src/symbol.rs | 1 + .../crates/intern/src/symbol/symbols.rs | 4 +++ 11 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 092c0a1dfdf81..86c3e0f041d39 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -1,10 +1,9 @@ //! Describes items defined or visible (ie, imported) in a certain scope. //! This is shared between modules and blocks. -use std::collections::hash_map::Entry; - use base_db::CrateId; use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId}; +use indexmap::map::Entry; use itertools::Itertools; use la_arena::Idx; use once_cell::sync::Lazy; @@ -17,8 +16,8 @@ use crate::{ db::DefDatabase, per_ns::PerNs, visibility::{Visibility, VisibilityExplicitness}, - AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, - ModuleDefId, ModuleId, TraitId, UseId, + AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId, + Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId, }; #[derive(Debug, Default)] @@ -67,9 +66,9 @@ pub struct ItemScope { /// Defs visible in this scope. This includes `declarations`, but also /// imports. The imports belong to this module and can be resolved by using them on /// the `use_imports_*` fields. - types: FxHashMap)>, - values: FxHashMap)>, - macros: FxHashMap)>, + types: FxIndexMap)>, + values: FxIndexMap)>, + macros: FxIndexMap)>, unresolved: FxHashSet, /// The defs declared in this scope. Each def has a single scope where it is @@ -118,7 +117,7 @@ struct DeriveMacroInvocation { derive_call_ids: SmallVec<[Option; 1]>, } -pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { +pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { BuiltinType::all_builtin_types() .iter() .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None))) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 8e7ef48112f04..b0543727c27d0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -323,7 +323,7 @@ pub struct ModuleData { /// /// [`None`] for block modules because they are always its `DefMap`'s root. pub parent: Option, - pub children: FxHashMap, + pub children: FxIndexMap, pub scope: ItemScope, } @@ -593,10 +593,8 @@ impl DefMap { self.data.extern_prelude.iter().map(|(name, &def)| (name, def)) } - pub(crate) fn macro_use_prelude( - &self, - ) -> impl Iterator))> + '_ { - self.macro_use_prelude.iter().map(|(name, &def)| (name, def)) + pub(crate) fn macro_use_prelude(&self) -> &FxHashMap)> { + &self.macro_use_prelude } pub(crate) fn resolve_path( @@ -668,7 +666,7 @@ impl ModuleData { origin, visibility, parent: None, - children: FxHashMap::default(), + children: Default::default(), scope: ItemScope::default(), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index d278b75e8158a..14d497b3a11c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -1348,6 +1348,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } .keys() .map(|name| name.display(&db).to_string()) .sorted() + .sorted() .join("\n"); expect![[r#" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index c196f00281639..f0f2210ec2c51 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -4,6 +4,7 @@ use std::{fmt, iter, mem}; use base_db::CrateId; use hir_expand::{name::Name, MacroDefId}; use intern::{sym, Interned}; +use itertools::Itertools as _; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; use triomphe::Arc; @@ -497,9 +498,11 @@ impl Resolver { res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); }) }); - def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| { - res.add(name, ScopeDef::ModuleDef(def.into())); - }); + def_map.macro_use_prelude().iter().sorted_by_key(|&(k, _)| k.clone()).for_each( + |(name, &(def, _extern_crate))| { + res.add(name, ScopeDef::ModuleDef(def.into())); + }, + ); def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| { res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into()))); }); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index f17635972b78c..37c5d7ce7efd7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -1637,8 +1637,8 @@ mod bar { #[test] fn local_inline_import_has_alias() { - // FIXME - check_assist_not_applicable( + // FIXME wrong import + check_assist( auto_import, r#" struct S(T); @@ -1647,14 +1647,24 @@ use S as IoResult; mod foo { pub fn bar() -> S$0<()> {} } +"#, + r#" +struct S(T); +use S as IoResult; + +mod foo { + use crate::S; + + pub fn bar() -> S<()> {} +} "#, ); } #[test] fn alias_local() { - // FIXME - check_assist_not_applicable( + // FIXME wrong import + check_assist( auto_import, r#" struct S(T); @@ -1663,6 +1673,16 @@ use S as IoResult; mod foo { pub fn bar() -> IoResult$0<()> {} } +"#, + r#" +struct S(T); +use S as IoResult; + +mod foo { + use crate::S; + + pub fn bar() -> IoResult<()> {} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 5512ac2153460..559a9bcba2961 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -31,7 +31,7 @@ pub(crate) fn format_string( }; let source_range = TextRange::new(brace_offset, cursor); - ctx.locals.iter().for_each(|(name, _)| { + ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()).for_each(|(name, _)| { CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str()) .add_to(acc, ctx.db); }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index c15e91c404839..cdfe23170176c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -259,7 +259,7 @@ pub(super) fn add_call_parens<'b>( fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'static str { if let Some(derefed_ty) = ty.remove_ref() { - for (name, local) in ctx.locals.iter() { + for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { if ty.is_mutable_reference() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 7d9c1ed98ac59..eacec018c72b9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -767,8 +767,8 @@ fn main() { } "#, expect![[r#" - fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED + fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 4814394de6b29..38cb4a162c28c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -15,7 +15,7 @@ use syntax::{ use crate::{ helpers::item_name, items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, - RootDatabase, + FxIndexSet, RootDatabase, }; /// A candidate for import, derived during various IDE activities: @@ -262,7 +262,7 @@ impl ImportAssets { let scope = match sema.scope(&self.candidate_node) { Some(it) => it, - None => return >::default().into_iter(), + None => return >::default().into_iter(), }; let krate = self.module_with_candidate.krate(); @@ -319,7 +319,7 @@ fn path_applicable_imports( path_candidate: &PathImportCandidate, mod_path: impl Fn(ItemInNs) -> Option + Copy, scope_filter: impl Fn(ItemInNs) -> bool + Copy, -) -> FxHashSet { +) -> FxIndexSet { let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered(); match &path_candidate.qualifier { @@ -500,7 +500,7 @@ fn trait_applicable_items( trait_assoc_item: bool, mod_path: impl Fn(ItemInNs) -> Option, scope_filter: impl Fn(hir::Trait) -> bool, -) -> FxHashSet { +) -> FxIndexSet { let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered(); let db = sema.db; @@ -566,7 +566,7 @@ fn trait_applicable_items( definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate() }); - let mut located_imports = FxHashSet::default(); + let mut located_imports = FxIndexSet::default(); let mut trait_import_paths = FxHashMap::default(); if trait_assoc_item { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 9e275713876c0..a3cc5c3d6a3d6 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -37,6 +37,7 @@ const _: () = /// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or /// `Arc>` but its size is that of a thin pointer. The active variant is encoded as a tag /// in the LSB of the alignment niche. +// Note, Ideally this would encode a `ThinArc` and `ThinRef`/`ThinConstPtr` instead of the double indirection. #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] struct TaggedArcPtr { packed: NonNull<*const str>, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 6304155ed76ad..064335471e95a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -13,6 +13,10 @@ use crate::{ macro_rules! define_symbols { (@WITH_NAME: $($alias:ident = $value:literal),* $(,)? @PLAIN: $($name:ident),* $(,)?) => { + // Ideally we would be emitting `const` here, but then we no longer have stable addresses + // which is what we are relying on for equality! In the future if consts can refer to + // statics we should swap these for `const`s and have the the string literal being pointed + // to be statics to refer to such that their address is stable. $( pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; )* From 6af77a624ea72427a6bb53dac89053fc95ee3329 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 7 Jul 2024 17:47:38 +0200 Subject: [PATCH 104/806] Encode ident rawness and literal kind separately in tt::Leaf --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/hir-def/src/attr.rs | 2 +- .../crates/hir-def/src/hir/format_args.rs | 2 +- .../macro_expansion_tests/builtin_fn_macro.rs | 8 +- .../macro_expansion_tests/mbe/regression.rs | 4 +- .../crates/hir-def/src/nameres/collector.rs | 4 +- .../crates/hir-expand/src/attrs.rs | 44 ++-- .../hir-expand/src/builtin_derive_macro.rs | 3 +- .../crates/hir-expand/src/builtin_fn_macro.rs | 199 +++++++++-------- .../crates/hir-expand/src/fixup.rs | 23 +- .../crates/hir-expand/src/lib.rs | 2 +- .../crates/hir-expand/src/mod_path.rs | 10 +- .../crates/hir-expand/src/name.rs | 11 +- .../crates/hir-expand/src/quote.rs | 31 ++- .../rust-analyzer/crates/hir/src/attrs.rs | 8 +- .../crates/ide-db/src/documentation.rs | 17 +- .../rust-analyzer/crates/ide/src/hover.rs | 2 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 1 + .../rust-analyzer/crates/mbe/src/benchmark.rs | 15 +- .../crates/mbe/src/expander/transcriber.rs | 24 ++- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 13 +- .../rust-analyzer/crates/mbe/src/parser.rs | 12 +- .../crates/mbe/src/syntax_bridge.rs | 188 +++++++++++----- .../crates/mbe/src/to_parser_input.rs | 26 +-- .../crates/proc-macro-api/src/msg.rs | 34 ++- .../crates/proc-macro-api/src/msg/flat.rs | 124 +++++++++-- .../proc-macro-test/imp/src/lib.rs | 1 - .../crates/proc-macro-srv/src/server_impl.rs | 75 +++---- .../src/server_impl/rust_analyzer_span.rs | 39 ++-- .../src/server_impl/token_id.rs | 38 ++-- .../crates/proc-macro-srv/src/tests/mod.rs | 104 ++++----- .../crates/salsa/tests/macros.rs | 1 + src/tools/rust-analyzer/crates/tt/src/lib.rs | 202 ++++++++++++++++-- 33 files changed, 858 insertions(+), 410 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b165697724e05..e43f712a6e230 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1046,6 +1046,7 @@ dependencies = [ "arrayvec", "cov-mark", "parser", + "ra-ap-rustc_lexer", "rustc-hash", "smallvec", "span", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index aacfb07319819..5bd60c58e9b7e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -605,7 +605,7 @@ impl<'attr> AttrQuery<'attr> { .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text), _ => None } }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index d6dd5df37366a..cf176e86dbd5d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -250,7 +250,7 @@ pub(crate) fn parse( } } ArgRef::Name(name, span) => { - let name = Name::new(name, call_ctx); + let name = Name::new(name, tt::IdentIsRaw::No, call_ctx); if let Some((index, _)) = args.by_name(&name) { record_usage(name, span); // Name found in `args`, so we resolve it to its index. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 9596100b60e13..ab8bb8bd4c6fc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -460,13 +460,13 @@ fn test_concat_expand() { #[rustc_builtin_macro] macro_rules! concat {} -fn main() { concat!("fo", "o", 0, r#"bar"#, "\n", false, '"', '\0'); } +fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', '\0'); } "##, expect![[r##" #[rustc_builtin_macro] macro_rules! concat {} -fn main() { "foo0bar\nfalse\"\u{0}"; } +fn main() { "foo0\"bar\"\nfalse\"\u{0}"; } "##]], ); } @@ -478,13 +478,13 @@ fn test_concat_bytes_expand() { #[rustc_builtin_macro] macro_rules! concat_bytes {} -fn main() { concat_bytes!(b'A', b"BC", [68, b'E', 70]); } +fn main() { concat_bytes!(b'A', b"BC\"", [68, b'E', 70], br#"G""#,b'\0'); } "##, expect![[r#" #[rustc_builtin_macro] macro_rules! concat_bytes {} -fn main() { [b'A', 66, 67, 68, b'E', 70]; } +fn main() { b"ABC\"DEFG\"\x00"; } "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 4aad53c3bd71c..252b0bb1b59b8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -1058,7 +1058,7 @@ macro_rules! concat {} macro_rules! line {} fn main() { - "event 0u32"; + "event 0"; } "##]], @@ -1084,7 +1084,7 @@ fn main() { macro_rules! concat_bytes {} fn main() { - let x = /* error: unexpected token in input */[]; + let x = /* error: unexpected token in input */b""; } "#]], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index f14679f6c2f14..13abcce78ead4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -82,7 +82,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI .iter() .enumerate() .map(|(idx, it)| { - let name = Name::new(&it.name, ctx); + let name = Name::new(&it.name, tt::IdentIsRaw::No, ctx); ( name, if !db.expand_proc_attr_macros() { @@ -2144,7 +2144,7 @@ impl ModCollector<'_, '_> { let name; let name = match attrs.by_key("rustc_builtin_macro").string_value_with_span() { Some((it, span)) => { - name = Name::new(it, span.ctx); + name = Name::new(it, tt::IdentIsRaw::No, span.ctx); &name } None => { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 36636a228f147..4fce7c1fde13f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -5,11 +5,14 @@ use base_db::CrateId; use cfg::CfgExpr; use either::Either; use intern::{sym, Interned}; -use mbe::{syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, Punct}; +use mbe::{ + desugar_doc_comment_text, syntax_node_to_token_tree, token_to_literal, DelimiterKind, + DocCommentDesugarMode, Punct, +}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; use syntax::unescape; -use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; +use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode}; use triomphe::ThinArc; use crate::name::Name; @@ -53,11 +56,15 @@ impl RawAttrs { } Either::Right(comment) => comment.doc_comment().map(|doc| { let span = span_map.span_for_range(comment.syntax().text_range()); + let (text, kind) = + desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro); Attr { id, input: Some(Box::new(AttrInput::Literal(tt::Literal { - text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))), + text, span, + kind, + suffix: None, }))), path: Interned::new(ModPath::from(Name::new_symbol( sym::doc.clone(), @@ -78,10 +85,6 @@ impl RawAttrs { RawAttrs { entries } } - fn escape_chars(s: &str) -> String { - s.replace('\\', r#"\\"#).replace('"', r#"\""#) - } - pub fn from_attrs_owner( db: &dyn ExpandDatabase, owner: InFile<&dyn ast::HasAttrs>, @@ -238,10 +241,8 @@ impl Attr { })?); let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { - Some(Box::new(AttrInput::Literal(tt::Literal { - text: lit.token().text().into(), - span, - }))) + let token = lit.token(); + Some(Box::new(AttrInput::Literal(token_to_literal(token.text().into(), span)))) } else if let Some(tt) = ast.token_tree() { let tree = syntax_node_to_token_tree( tt.syntax(), @@ -310,12 +311,11 @@ impl Attr { /// #[path = "string"] pub fn string_value(&self) -> Option<&str> { match self.input.as_deref()? { - AttrInput::Literal(it) => match it.text.strip_prefix('r') { - Some(it) => it.trim_matches('#'), - None => it.text.as_str(), - } - .strip_prefix('"')? - .strip_suffix('"'), + AttrInput::Literal(tt::Literal { + text, + kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), + .. + }) => Some(text), _ => None, } } @@ -336,12 +336,10 @@ impl Attr { pub fn string_value_unescape(&self) -> Option> { match self.input.as_deref()? { - AttrInput::Literal(it) => match it.text.strip_prefix('r') { - Some(it) => { - it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed) - } - None => it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape), - }, + AttrInput::Literal(tt::Literal { text, kind: tt::LitKind::StrRaw(_), .. }) => { + Some(Cow::Borrowed(text)) + } + AttrInput::Literal(tt::Literal { text, kind: tt::LitKind::Str, .. }) => unescape(text), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 269e9f308c2f6..180d8f05627f4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -370,7 +370,8 @@ fn name_to_token( ExpandError::other("missing name") })?; let span = token_map.span_at(name.syntax().text_range().start()); - let name_token = tt::Ident { span, text: name.text().into() }; + + let name_token = tt::Ident::new(name.text().as_ref(), span); Ok(name_token) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 97867dfc66e92..32befb7a7f7df 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,13 +1,14 @@ //! Builtin macro +use ::tt::SmolStr; use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; use intern::sym; -use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; -use syntax::ast::{self, AstToken}; +use stdx::format_to; +use syntax::unescape::{unescape_byte, unescape_char, unescape_unicode, Mode}; use crate::{ db::ExpandDatabase, @@ -177,8 +178,10 @@ fn line_expand( ExpandResult::ok(tt::Subtree { delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text: "0u32".into(), + text: "0".into(), span, + kind: tt::LitKind::Integer, + suffix: Some(Box::new("u32".into())), }))]), }) } @@ -444,27 +447,6 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { } } -fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> { - let span = lit.span; - let lit = ast::make::tokens::literal(&lit.to_string()); - let token = ast::String::cast(lit)?; - token.value().ok().map(|it| (it.into_owned(), span)) -} - -fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> { - let span = lit.span; - let lit = ast::make::tokens::literal(&lit.to_string()); - let token = ast::Char::cast(lit)?; - token.value().ok().zip(Some(span)) -} - -fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec, Span)> { - let span = lit.span; - let lit = ast::make::tokens::literal(&lit.to_string()); - let token = ast::ByteString::cast(lit)?; - token.value().ok().map(|it| (it.into_owned(), span)) -} - fn compile_error_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, @@ -472,10 +454,16 @@ fn compile_error_expand( span: Span, ) -> ExpandResult { let err = match &*tt.token_trees { - [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) { - Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()), - None => ExpandError::other("`compile_error!` argument must be a string"), - }, + [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text, + span: _, + kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), + suffix: _, + }))] => + // FIXME: Use the span here! + { + ExpandError::other(Box::from(&*unescape_str(text))) + } _ => ExpandError::other("`compile_error!` argument must be a string"), }; @@ -507,20 +495,33 @@ fn concat_expand( } } } - match t { tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. - if let Some((c, span)) = unquote_char(it) { - text.push(c); - record_span(span); - } else { - let (component, span) = - unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span)); - text.push_str(&component); - record_span(span); + match it.kind { + tt::LitKind::Char => { + if let Ok(c) = unescape_char(&it.text) { + text.extend(c.escape_default()); + } + record_span(it.span); + } + tt::LitKind::Integer | tt::LitKind::Float => format_to!(text, "{}", it.text), + tt::LitKind::Str => { + text.push_str(&it.text); + record_span(it.span); + } + tt::LitKind::StrRaw(_) => { + format_to!(text, "{}", it.text.escape_debug()); + record_span(it.span); + } + tt::LitKind::Byte + | tt::LitKind::ByteStr + | tt::LitKind::ByteStrRaw(_) + | tt::LitKind::CStr + | tt::LitKind::CStrRaw(_) + | tt::LitKind::Err(_) => err = Some(ExpandError::other("unexpected literal")), } } // handle boolean literals @@ -544,9 +545,9 @@ fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - call_site: Span, + _: Span, ) -> ExpandResult { - let mut bytes = Vec::new(); + let mut bytes = String::new(); let mut err = None; let mut span: Option = None; let mut record_span = |s: Span| match &mut span { @@ -556,14 +557,21 @@ fn concat_bytes_expand( }; for (i, t) in tt.token_trees.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - let token = ast::make::tokens::literal(&lit.to_string()); - record_span(lit.span); - match token.kind() { - syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()), - syntax::SyntaxKind::BYTE_STRING => { - let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it); - components.into_iter().for_each(|it| bytes.push(it.to_string())); + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, span, kind, suffix: _ })) => { + record_span(*span); + match kind { + tt::LitKind::Byte => { + if let Ok(b) = unescape_byte(text) { + bytes.extend( + b.escape_ascii().filter_map(|it| char::from_u32(it as u32)), + ); + } + } + tt::LitKind::ByteStr => { + bytes.push_str(text); + } + tt::LitKind::ByteStrRaw(_) => { + bytes.extend(text.escape_debug()); } _ => { err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); @@ -584,51 +592,49 @@ fn concat_bytes_expand( } } } - let value = tt::Subtree { - delimiter: tt::Delimiter { - open: call_site, - close: call_site, - kind: tt::DelimiterKind::Bracket, - }, - token_trees: { - Itertools::intersperse_with( - bytes.into_iter().map(|it| { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text: it.into(), - span: span.unwrap_or(call_site), - })) - }), - || { - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char: ',', - spacing: tt::Spacing::Alone, - span: call_site, - })) - }, - ) - .collect() + let span = span.unwrap_or(tt.delimiter.open); + ExpandResult { + value: tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(span), + token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text: bytes.into(), + span, + kind: tt::LitKind::ByteStr, + suffix: None, + }))] + .into(), }, - }; - ExpandResult { value, err } + err, + } } fn concat_bytes_expand_subtree( tree: &tt::Subtree, - bytes: &mut Vec, + bytes: &mut String, mut record_span: impl FnMut(Span), ) -> Result<(), ExpandError> { for (ti, tt) in tree.token_trees.iter().enumerate() { match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => { - let lit = ast::make::tokens::literal(&it.to_string()); - match lit.kind() { - syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => { - record_span(it.span); - bytes.push(lit.text().to_owned()) - } - _ => { - return Err(mbe::ExpandError::UnexpectedToken.into()); - } + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text, + span, + kind: tt::LitKind::Byte, + suffix: _, + })) => { + if let Ok(b) = unescape_byte(text) { + bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); + } + record_span(*span); + } + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text, + span, + kind: tt::LitKind::Integer, + suffix: _, + })) => { + record_span(*span); + if let Ok(b) = text.parse::() { + bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); } } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), @@ -660,7 +666,7 @@ fn concat_idents_expand( } } // FIXME merge spans - let ident = tt::Ident { text: ident.into(), span }; + let ident = tt::Ident { text: ident.into(), span, is_raw: tt::IdentIsRaw::No }; ExpandResult { value: quote!(span =>#ident), err } } @@ -683,11 +689,16 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> { +fn parse_string(tt: &tt::Subtree) -> Result<(SmolStr, Span), ExpandError> { tt.token_trees .first() .and_then(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text, + span, + kind: tt::LitKind::Str, + suffix: _, + })) => Some((unescape_str(text), *span)), _ => None, }) .ok_or(mbe::ExpandError::ConversionError.into()) @@ -738,6 +749,8 @@ fn include_bytes_expand( token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: r#"b"""#.into(), span, + kind: tt::LitKind::ByteStrRaw(1), + suffix: None, }))]), }; ExpandResult::ok(res) @@ -848,3 +861,17 @@ fn quote_expand( ExpandError::other("quote! is not implemented"), ) } + +fn unescape_str(s: &SmolStr) -> SmolStr { + if s.contains('\\') { + let mut buf = String::with_capacity(s.len()); + unescape_unicode(s, Mode::Str, &mut |_, c| { + if let Ok(c) = c { + buf.push(c) + } + }); + buf.into() + } else { + s.clone() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 9fdf4aa4f7cc7..2896afed084fd 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -86,6 +86,7 @@ pub(crate) fn fixup_syntax( anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, ctx: span.ctx, }, + is_raw: tt::IdentIsRaw::No, }); append.insert(node.clone().into(), vec![replacement]); preorder.skip_subtree(); @@ -101,6 +102,7 @@ pub(crate) fn fixup_syntax( Leaf::Ident(Ident { text: "__ra_fixup".into(), span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }), ]); } @@ -137,7 +139,8 @@ pub(crate) fn fixup_syntax( append.insert(if_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }), ]); } @@ -167,7 +170,8 @@ pub(crate) fn fixup_syntax( append.insert(while_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }), ]); } @@ -214,7 +218,8 @@ pub(crate) fn fixup_syntax( append.insert(match_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }), ]); } @@ -248,7 +253,8 @@ pub(crate) fn fixup_syntax( ].map(|text| Leaf::Ident(Ident { text: text.into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }), ); @@ -281,7 +287,8 @@ pub(crate) fn fixup_syntax( append.insert(colon.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }) ]); } @@ -293,7 +300,8 @@ pub(crate) fn fixup_syntax( append.insert(colon.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }) ]); } @@ -326,7 +334,8 @@ pub(crate) fn fixup_syntax( append.insert(node.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span(node_range) + span: fake_span(node_range), + is_raw: tt::IdentIsRaw::No }) ]); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index e7c34e51e857a..c4921da61004c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -59,7 +59,7 @@ pub use span::{HirFileId, MacroCallId, MacroFileId}; pub mod tt { pub use span::Span; - pub use tt::{DelimiterKind, Spacing}; + pub use tt::{DelimiterKind, IdentIsRaw, LitKind, Spacing}; pub type Delimiter = ::tt::Delimiter; pub type DelimSpan = ::tt::DelimSpan; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index ed7d551888756..907e939153ba7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -316,15 +316,15 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option PathKind::Abs, _ => return None, }, - tt::Leaf::Ident(tt::Ident { text, span }) if text == "$crate" => { + tt::Leaf::Ident(tt::Ident { text, span, .. }) if text == "$crate" => { resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate) } tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::SELF, tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => { let mut deg = 1; - while let Some(tt::Leaf::Ident(tt::Ident { text, span, .. })) = leaves.next() { + while let Some(tt::Leaf::Ident(tt::Ident { text, span, is_raw })) = leaves.next() { if text != "super" { - segments.push(Name::new(text, span.ctx)); + segments.push(Name::new(text, *is_raw, span.ctx)); break; } deg += 1; @@ -333,13 +333,13 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option PathKind::Crate, tt::Leaf::Ident(ident) => { - segments.push(Name::new(&ident.text, ident.span.ctx)); + segments.push(Name::new(&ident.text, ident.is_raw, ident.span.ctx)); PathKind::Plain } _ => return None, }; segments.extend(leaves.filter_map(|leaf| match leaf { - ::tt::Leaf::Ident(ident) => Some(Name::new(&ident.text, ident.span.ctx)), + ::tt::Leaf::Ident(ident) => Some(Name::new(&ident.text, ident.is_raw, ident.span.ctx)), _ => None, })); Some(ModPath { kind, segments }) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 67e73f7fc284c..fce9df6722b47 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -82,9 +82,16 @@ impl Name { Name { symbol: Symbol::intern(text), ctx: () } } - pub fn new(text: &str, ctx: SyntaxContextId) -> Name { + pub fn new(text: &str, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Name { _ = ctx; - Name { symbol: Symbol::intern(text), ctx: () } + Name { + symbol: if raw.yes() { + Symbol::intern(&format_smolstr!("{}{text}", raw.as_str())) + } else { + Symbol::intern(text) + }, + ctx: (), + } } pub fn new_tuple_field(idx: usize) -> Name { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index f4fc3b7b3e2da..f1d28450b31e8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -3,12 +3,12 @@ use intern::Symbol; use span::Span; -use syntax::format_smolstr; +use tt::IdentIsRaw; use crate::name::Name; pub(crate) const fn dollar_crate(span: Span) -> tt::Ident { - tt::Ident { text: syntax::SmolStr::new_static("$crate"), span } + tt::Ident { text: syntax::SmolStr::new_static("$crate"), span, is_raw: tt::IdentIsRaw::No } } // A helper macro quote macro @@ -101,6 +101,7 @@ macro_rules! __quote { crate::tt::Leaf::Ident(crate::tt::Ident { text: stringify!($tt).into(), span: $span, + is_raw: tt::IdentIsRaw::No, }).into() }] }; @@ -209,23 +210,30 @@ macro_rules! impl_to_to_tokentrees { } impl_to_to_tokentrees! { - span: u32 => self { crate::tt::Literal{text: self.to_string().into(), span} }; - span: usize => self { crate::tt::Literal{text: self.to_string().into(), span} }; - span: i32 => self { crate::tt::Literal{text: self.to_string().into(), span} }; - span: bool => self { crate::tt::Ident{text: self.to_string().into(), span} }; + span: u32 => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; + span: usize => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; + span: i32 => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; + span: bool => self { crate::tt::Ident{text: self.to_string().into(), span, is_raw: tt::IdentIsRaw::No } }; _span: crate::tt::Leaf => self { self }; _span: crate::tt::Literal => self { self }; _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; - span: &str => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}}; - span: String => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}}; - span: Name => self { crate::tt::Ident{text: self.to_smol_str(), span}}; - span: Symbol => self { crate::tt::Ident{text: self.as_str().into(), span}}; + span: &str => self { crate::tt::Literal{text: (*self).into(), span, kind: tt::LitKind::Str, suffix: None }}; + span: String => self { crate::tt::Literal{text: self.into(), span, kind: tt::LitKind::Str, suffix: None }}; + span: Name => self { + let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); + crate::tt::Ident{text: s.into(), span, is_raw } + }; + span: Symbol => self { + let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); + crate::tt::Ident{text: s.into(), span, is_raw } + }; } #[cfg(test)] mod tests { use crate::tt; + use ::tt::IdentIsRaw; use base_db::FileId; use expect_test::expect; use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; @@ -259,7 +267,8 @@ mod tests { } fn mk_ident(name: &str) -> crate::tt::Ident { - crate::tt::Ident { text: name.into(), span: DUMMY } + let (is_raw, s) = IdentIsRaw::split_from_symbol(name); + crate::tt::Ident { text: s.into(), span: DUMMY, is_raw } } #[test] diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index af60c233e5519..02d92620e05b6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -328,9 +328,11 @@ fn doc_modpath_from_str(link: &str) -> Option { }; let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() { Ok(idx) => Name::new_tuple_field(idx), - Err(_) => { - Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT) - } + Err(_) => Name::new( + segment.split_once('<').map_or(segment, |it| it.0), + tt::IdentIsRaw::No, + SyntaxContextId::ROOT, + ), }); Some(ModPath::from_segments(kind, parts)) }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index 58e77b95c3292..1b9b78f69182a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -269,12 +269,13 @@ fn get_doc_string_in_attr(it: &ast::Attr) -> Option { } fn doc_indent(attrs: &hir::Attrs) -> usize { - attrs - .by_key("doc") - .attrs() - .filter_map(|attr| attr.string_value()) // no need to use unescape version here - .flat_map(|s| s.lines()) - .filter_map(|line| line.chars().position(|c| !c.is_whitespace())) - .min() - .unwrap_or(0) + let mut min = !0; + for val in attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape()) { + if let Some(m) = + val.lines().filter_map(|line| line.chars().position(|c| !c.is_whitespace())).min() + { + min = min.min(m); + } + } + min } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 2006baa30a8c3..701374616ad67 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -240,7 +240,7 @@ fn hover_simple( .flatten() .unique_by(|&(def, _, _)| def) .map(|(def, macro_arm, node)| { - hover_for_definition(sema, file_id, def, &node, macro_arm, config) + dbg!(hover_for_definition(sema, file_id, def, &node, macro_arm, config)) }) .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { acc.actions.extend(actions); diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 18444018e1b68..7ce8aadfb3619 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -17,6 +17,7 @@ rustc-hash.workspace = true smallvec.workspace = true tracing.workspace = true arrayvec.workspace = true +ra-ap-rustc_lexer.workspace = true # local deps syntax.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 27dbc84a2b1aa..6a2f1c236869d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -226,13 +226,24 @@ fn invocation_fixtures( *seed } fn make_ident(ident: &str) -> tt::TokenTree { - tt::Leaf::Ident(tt::Ident { span: DUMMY, text: SmolStr::new(ident) }).into() + tt::Leaf::Ident(tt::Ident { + span: DUMMY, + text: SmolStr::new(ident), + is_raw: tt::IdentIsRaw::No, + }) + .into() } fn make_punct(char: char) -> tt::TokenTree { tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into() } fn make_literal(lit: &str) -> tt::TokenTree { - tt::Leaf::Literal(tt::Literal { span: DUMMY, text: SmolStr::new(lit) }).into() + tt::Leaf::Literal(tt::Literal { + span: DUMMY, + text: SmolStr::new(lit), + kind: tt::LitKind::Str, + suffix: None, + }) + .into() } fn make_subtree( kind: tt::DelimiterKind, diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index c09cbd1d071ea..e3359865cb276 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -2,7 +2,7 @@ //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` use span::Span; -use syntax::SmolStr; +use syntax::{format_smolstr, SmolStr}; use tt::Delimiter; use crate::{ @@ -99,6 +99,7 @@ impl Bindings { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: SmolStr::new_static("missing"), span, + is_raw: tt::IdentIsRaw::No, }))) } MetaVarKind::Lifetime => { @@ -113,6 +114,7 @@ impl Bindings { tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: SmolStr::new_static("missing"), span, + is_raw: tt::IdentIsRaw::No, })), ]), })) @@ -121,6 +123,7 @@ impl Bindings { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: SmolStr::new_static("\"missing\""), span, + is_raw: tt::IdentIsRaw::No, }))) } } @@ -236,8 +239,10 @@ fn expand_subtree( ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); arena.push( tt::Leaf::Literal(tt::Literal { - text: index.to_string().into(), + text: format_smolstr!("{index}"), span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, }) .into(), ); @@ -249,8 +254,10 @@ fn expand_subtree( }); arena.push( tt::Leaf::Literal(tt::Literal { - text: length.to_string().into(), + text: format_smolstr!("{length}"), span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, }) .into(), ); @@ -314,8 +321,10 @@ fn expand_subtree( }; arena.push( tt::Leaf::Literal(tt::Literal { - text: c.to_string().into(), + text: format_smolstr!("{c}"), span: ctx.call_site, + suffix: None, + kind: tt::LitKind::Integer, }) .into(), ); @@ -363,7 +372,12 @@ fn expand_var( token_trees: Box::new([ tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }) .into(), - tt::Leaf::from(tt::Ident { text: v.clone(), span: id }).into(), + tt::Leaf::from(tt::Ident { + text: v.clone(), + span: id, + is_raw: tt::IdentIsRaw::No, + }) + .into(), ]), } .into(); diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index b06c6cee12dbd..8ab9269e9523a 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -6,6 +6,13 @@ //! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + mod expander; mod parser; mod syntax_bridge; @@ -27,9 +34,9 @@ pub use ::parser::TopEntryPoint; pub use tt::{Delimiter, DelimiterKind, Punct}; pub use crate::syntax_bridge::{ - parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, - syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node, - DocCommentDesugarMode, SpanMapper, + desugar_doc_comment_text, parse_exprs_with_sep, parse_to_token_tree, + parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified, + token_to_literal, token_tree_to_syntax_node, DocCommentDesugarMode, SpanMapper, }; pub use crate::syntax_bridge::dummy_test_span_utils::*; diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index 5c499c06b1525..18af35c1e2900 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -205,7 +205,11 @@ fn next_op( tt::TokenTree::Leaf(leaf) => match leaf { tt::Leaf::Ident(ident) if ident.text == "crate" => { // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. - Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span }) + Op::Ident(tt::Ident { + text: "$crate".into(), + span: ident.span, + is_raw: tt::IdentIsRaw::No, + }) } tt::Leaf::Ident(ident) => { let kind = eat_fragment_kind(edition, src, mode)?; @@ -380,9 +384,11 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { if src.len() == 0 { Ok(0) - } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { + } else if let tt::Leaf::Literal(tt::Literal { text, suffix: None, .. }) = + src.expect_literal()? + { // Suffixes are not allowed. - lit.text.parse().map_err(|_| ()) + text.parse().map_err(|_| ()) } else { Err(()) } diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 73a04f00d9306..3feddba21061f 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -4,11 +4,11 @@ use std::fmt; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, SpanAnchor, SpanData, SpanMap}; -use stdx::{never, non_empty_vec::NonEmptyVec}; +use stdx::{format_to, itertools::Itertools, never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, - AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind, - SyntaxKind::*, + format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, + SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; use tt::{ @@ -317,18 +317,29 @@ where .into() } kind => { - macro_rules! make_leaf { - ($i:ident) => { - tt::$i { span: conv.span_for(abs_range), text: token.to_text(conv) } - .into() + macro_rules! make_ident { + () => { + tt::Ident { + span: conv.span_for(abs_range), + text: token.to_text(conv), + is_raw: tt::IdentIsRaw::No, + } + .into() }; } let leaf: tt::Leaf<_> = match kind { - T![true] | T![false] => make_leaf!(Ident), - IDENT => make_leaf!(Ident), - UNDERSCORE => make_leaf!(Ident), - k if k.is_keyword() => make_leaf!(Ident), - k if k.is_literal() => make_leaf!(Literal), + T![true] | T![false] => make_ident!(), + IDENT => { + let text = token.to_text(conv); + tt::Ident::new(text, conv.span_for(abs_range)).into() + } + UNDERSCORE => make_ident!(), + k if k.is_keyword() => make_ident!(), + k if k.is_literal() => { + let text = token.to_text(conv); + let span = conv.span_for(abs_range); + token_to_literal(text, span).into() + } LIFETIME_IDENT => { let apostrophe = tt::Leaf::from(tt::Punct { char: '\'', @@ -344,6 +355,7 @@ where abs_range.start() + TextSize::of('\''), abs_range.end(), )), + is_raw: tt::IdentIsRaw::No, }); token_trees.push(ident.into()); continue; @@ -388,6 +400,56 @@ where } } +pub fn token_to_literal(text: SmolStr, span: S) -> tt::Literal +where + S: Copy, +{ + use rustc_lexer::LiteralKind; + + let token = rustc_lexer::tokenize(&text).next_tuple(); + let Some((rustc_lexer::Token { + kind: rustc_lexer::TokenKind::Literal { kind, suffix_start }, + .. + },)) = token + else { + return tt::Literal { span, text, kind: tt::LitKind::Err(()), suffix: None }; + }; + + let (kind, start_offset, end_offset) = match kind { + LiteralKind::Int { .. } => (tt::LitKind::Integer, 0, 0), + LiteralKind::Float { .. } => (tt::LitKind::Float, 0, 0), + LiteralKind::Char { terminated } => (tt::LitKind::Char, 1, terminated as usize), + LiteralKind::Byte { terminated } => (tt::LitKind::Byte, 2, terminated as usize), + LiteralKind::Str { terminated } => (tt::LitKind::Str, 1, terminated as usize), + LiteralKind::ByteStr { terminated } => (tt::LitKind::ByteStr, 2, terminated as usize), + LiteralKind::CStr { terminated } => (tt::LitKind::CStr, 2, terminated as usize), + LiteralKind::RawStr { n_hashes } => ( + tt::LitKind::StrRaw(n_hashes.unwrap_or_default()), + 2 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawByteStr { n_hashes } => ( + tt::LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawCStr { n_hashes } => ( + tt::LitKind::CStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + }; + + let (lit, suffix) = text.split_at(suffix_start as usize); + let lit = &lit[start_offset..lit.len() - end_offset]; + let suffix = match suffix { + "" | "_" => None, + suffix => Some(Box::new(suffix.into())), + }; + + tt::Literal { span, text: lit.into(), kind, suffix } +} + fn is_single_token_op(kind: SyntaxKind) -> bool { matches!( kind, @@ -421,16 +483,10 @@ fn is_single_token_op(kind: SyntaxKind) -> bool { /// That is, strips leading `///` (or `/**`, etc) /// and strips the ending `*/` /// And then quote the string, which is needed to convert to `tt::Literal` -fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> SmolStr { - let prefix_len = comment.prefix().len(); - let mut text = &comment.text()[prefix_len..]; - - // Remove ending "*/" - if comment.kind().shape == ast::CommentShape::Block { - text = &text[0..text.len() - 2]; - } - - let text = match mode { +/// +/// Note that proc-macros desugar with string literals where as macro_rules macros desugar with raw string literals. +pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (SmolStr, tt::LitKind) { + match mode { DocCommentDesugarMode::Mbe => { let mut num_of_hashes = 0; let mut count = 0; @@ -444,14 +500,13 @@ fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> Smol } // Quote raw string with delimiters - // Note that `tt::Literal` expect an escaped string - format!(r#"r{delim}"{text}"{delim}"#, delim = "#".repeat(num_of_hashes)) + (text.into(), tt::LitKind::StrRaw(num_of_hashes)) } // Quote string with delimiters - // Note that `tt::Literal` expect an escaped string - DocCommentDesugarMode::ProcMacro => format!(r#""{}""#, text.escape_debug()), - }; - text.into() + DocCommentDesugarMode::ProcMacro => { + (format_smolstr!("{}", text.escape_debug()), tt::LitKind::Str) + } + } } fn convert_doc_comment( @@ -463,8 +518,13 @@ fn convert_doc_comment( let comment = ast::Comment::cast(token.clone())?; let doc = comment.kind().doc?; - let mk_ident = - |s: &str| tt::TokenTree::from(tt::Leaf::from(tt::Ident { text: s.into(), span })); + let mk_ident = |s: &str| { + tt::TokenTree::from(tt::Leaf::from(tt::Ident { + text: s.into(), + span, + is_raw: tt::IdentIsRaw::No, + })) + }; let mk_punct = |c: char| { tt::TokenTree::from(tt::Leaf::from(tt::Punct { @@ -475,7 +535,15 @@ fn convert_doc_comment( }; let mk_doc_literal = |comment: &ast::Comment| { - let lit = tt::Literal { text: doc_comment_text(comment, mode), span }; + let prefix_len = comment.prefix().len(); + let mut text = &comment.text()[prefix_len..]; + + // Remove ending "*/" + if comment.kind().shape == ast::CommentShape::Block { + text = &text[0..text.len() - 2]; + } + let (text, kind) = desugar_doc_comment_text(text, mode); + let lit = tt::Literal { text, span, kind, suffix: None }; tt::TokenTree::from(tt::Leaf::from(lit)) }; @@ -902,16 +970,17 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { impl TtTreeSink<'_, Ctx> where - SpanData: Copy, + SpanData: Copy + fmt::Debug, { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. fn float_split(&mut self, has_pseudo_dot: bool) { let (text, span) = match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Literal(lit), _)) => { - (lit.text.as_str(), lit.span) - } - _ => unreachable!(), + Some(tt::buffer::TokenTreeRef::Leaf( + tt::Leaf::Literal(tt::Literal { text, span, kind: tt::LitKind::Float, suffix: _ }), + _, + )) => (text.as_str(), *span), + tt => unreachable!("{tt:?}"), }; // FIXME: Span splitting match text.split_once('.') { @@ -954,7 +1023,7 @@ where } let mut last = self.cursor; - for _ in 0..n_tokens { + 'tokens: for _ in 0..n_tokens { let tmp: u8; if self.cursor.eof() { break; @@ -962,23 +1031,36 @@ where last = self.cursor; let (text, span) = loop { break match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { - // Mark the range if needed - let (text, span) = match leaf { - tt::Leaf::Ident(ident) => (ident.text.as_str(), ident.span), - tt::Leaf::Punct(punct) => { - assert!(punct.char.is_ascii()); - tmp = punct.char as u8; - ( - std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(), - punct.span, - ) + Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => match leaf { + tt::Leaf::Ident(ident) => { + if ident.is_raw.yes() { + self.buf.push_str("r#"); + self.text_pos += TextSize::of("r#"); } - tt::Leaf::Literal(lit) => (lit.text.as_str(), lit.span), - }; - self.cursor = self.cursor.bump(); - (text, span) - } + let r = (ident.text.as_str(), ident.span); + self.cursor = self.cursor.bump(); + r + } + tt::Leaf::Punct(punct) => { + assert!(punct.char.is_ascii()); + tmp = punct.char as u8; + let r = ( + std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(), + punct.span, + ); + self.cursor = self.cursor.bump(); + r + } + tt::Leaf::Literal(lit) => { + let buf_l = self.buf.len(); + format_to!(self.buf, "{lit}"); + debug_assert_ne!(self.buf.len() - buf_l, 0); + self.text_pos += TextSize::new((self.buf.len() - buf_l) as u32); + self.token_map.push(self.text_pos, lit.span); + self.cursor = self.cursor.bump(); + continue 'tokens; + } + }, Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { self.cursor = self.cursor.subtree().unwrap(); match delim_to_str(subtree.delimiter.kind, false) { diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs index 3f70149aa5eae..bf5494d37149b 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs @@ -35,20 +35,21 @@ pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { match leaf { tt::Leaf::Literal(lit) => { - let is_negated = lit.text.starts_with('-'); - let inner_text = &lit.text[if is_negated { 1 } else { 0 }..]; - - let kind = parser::LexedStr::single_token(inner_text) - .map(|(kind, _error)| kind) - .filter(|kind| { - kind.is_literal() - && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER)) - }) - .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &lit)); - + let kind = match lit.kind { + tt::LitKind::Byte => SyntaxKind::BYTE, + tt::LitKind::Char => SyntaxKind::CHAR, + tt::LitKind::Integer => SyntaxKind::INT_NUMBER, + tt::LitKind::Float => SyntaxKind::FLOAT_NUMBER, + tt::LitKind::Str | tt::LitKind::StrRaw(_) => SyntaxKind::STRING, + tt::LitKind::ByteStr | tt::LitKind::ByteStrRaw(_) => { + SyntaxKind::BYTE_STRING + } + tt::LitKind::CStr | tt::LitKind::CStrRaw(_) => SyntaxKind::C_STRING, + tt::LitKind::Err(_) => SyntaxKind::ERROR, + }; res.push(kind); - if kind == FLOAT_NUMBER && !inner_text.ends_with('.') { + if kind == FLOAT_NUMBER && !lit.text.ends_with('.') { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access @@ -58,6 +59,7 @@ pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) tt::Leaf::Ident(ident) => match ident.text.as_ref() { "_" => res.push(T![_]), i if i.starts_with('\'') => res.push(LIFETIME_IDENT), + _ if ident.is_raw.yes() => res.push(IDENT), _ => match SyntaxKind::from_keyword(&ident.text) { Some(kind) => res.push(kind), None => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index fa3ba9bbfcd73..b5f3d0c3aacd5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -19,8 +19,10 @@ pub const VERSION_CHECK_VERSION: u32 = 1; pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2; pub const HAS_GLOBAL_SPANS: u32 = 3; pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4; +/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field +pub const EXTENDED_LEAF_DATA: u32 = 5; -pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT; +pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA; #[derive(Debug, Serialize, Deserialize)] pub enum Request { @@ -178,6 +180,7 @@ mod tests { anchor, ctx: SyntaxContextId::ROOT, }, + is_raw: tt::IdentIsRaw::No, } .into(), ), @@ -185,26 +188,28 @@ mod tests { Ident { text: "Foo".into(), span: Span { - range: TextRange::at(TextSize::new(5), TextSize::of("Foo")), + range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, ctx: SyntaxContextId::ROOT, }, + is_raw: tt::IdentIsRaw::Yes, } .into(), ), TokenTree::Leaf(Leaf::Literal(Literal { - text: "Foo".into(), - + text: "\"Foo\"".into(), span: Span { - range: TextRange::at(TextSize::new(8), TextSize::of("Foo")), + range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, ctx: SyntaxContextId::ROOT, }, + kind: tt::LitKind::Str, + suffix: None, })), TokenTree::Leaf(Leaf::Punct(Punct { char: '@', span: Span { - range: TextRange::at(TextSize::new(11), TextSize::of('@')), + range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, ctx: SyntaxContextId::ROOT, }, @@ -213,18 +218,27 @@ mod tests { TokenTree::Subtree(Subtree { delimiter: Delimiter { open: Span { - range: TextRange::at(TextSize::new(12), TextSize::of('{')), + range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, ctx: SyntaxContextId::ROOT, }, close: Span { - range: TextRange::at(TextSize::new(13), TextSize::of('}')), + range: TextRange::at(TextSize::new(19), TextSize::of('}')), anchor, ctx: SyntaxContextId::ROOT, }, kind: DelimiterKind::Brace, }, - token_trees: Box::new([]), + token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal { + text: "0".into(), + span: Span { + range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: tt::LitKind::Integer, + suffix: Some(Box::new("u32".into())), + }))]), }), ]); @@ -236,7 +250,7 @@ mod tests { ctx: SyntaxContextId::ROOT, }, close: Span { - range: TextRange::empty(TextSize::new(13)), + range: TextRange::empty(TextSize::new(19)), anchor, ctx: SyntaxContextId::ROOT, }, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index 11fd7596f2b52..7f5afdb7270fe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -43,7 +43,7 @@ use serde::{Deserialize, Serialize}; use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; use text_size::TextRange; -use crate::msg::ENCODE_CLOSE_SPAN_VERSION; +use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; pub type SpanDataIndexMap = indexmap::IndexSet>; @@ -108,6 +108,8 @@ struct SubtreeRepr { struct LiteralRepr { id: TokenId, text: u32, + suffix: u32, + kind: u16, } struct PunctRepr { @@ -119,6 +121,7 @@ struct PunctRepr { struct IdentRepr { id: TokenId, text: u32, + is_raw: bool, } impl FlatTree { @@ -147,9 +150,17 @@ impl FlatTree { } else { write_vec(w.subtree, SubtreeRepr::write) }, - literal: write_vec(w.literal, LiteralRepr::write), + literal: if version >= EXTENDED_LEAF_DATA { + write_vec(w.literal, LiteralRepr::write_with_kind) + } else { + write_vec(w.literal, LiteralRepr::write) + }, punct: write_vec(w.punct, PunctRepr::write), - ident: write_vec(w.ident, IdentRepr::write), + ident: if version >= EXTENDED_LEAF_DATA { + write_vec(w.ident, IdentRepr::write_with_rawness) + } else { + write_vec(w.ident, IdentRepr::write) + }, token_tree: w.token_tree, text: w.text, } @@ -176,9 +187,17 @@ impl FlatTree { } else { write_vec(w.subtree, SubtreeRepr::write) }, - literal: write_vec(w.literal, LiteralRepr::write), + literal: if version >= EXTENDED_LEAF_DATA { + write_vec(w.literal, LiteralRepr::write_with_kind) + } else { + write_vec(w.literal, LiteralRepr::write) + }, punct: write_vec(w.punct, PunctRepr::write), - ident: write_vec(w.ident, IdentRepr::write), + ident: if version >= EXTENDED_LEAF_DATA { + write_vec(w.ident, IdentRepr::write_with_rawness) + } else { + write_vec(w.ident, IdentRepr::write) + }, token_tree: w.token_tree, text: w.text, } @@ -195,9 +214,17 @@ impl FlatTree { } else { read_vec(self.subtree, SubtreeRepr::read) }, - literal: read_vec(self.literal, LiteralRepr::read), + literal: if version >= EXTENDED_LEAF_DATA { + read_vec(self.literal, LiteralRepr::read_with_kind) + } else { + read_vec(self.literal, LiteralRepr::read) + }, punct: read_vec(self.punct, PunctRepr::read), - ident: read_vec(self.ident, IdentRepr::read), + ident: if version >= EXTENDED_LEAF_DATA { + read_vec(self.ident, IdentRepr::read_with_rawness) + } else { + read_vec(self.ident, IdentRepr::read) + }, token_tree: self.token_tree, text: self.text, span_data_table, @@ -212,9 +239,17 @@ impl FlatTree { } else { read_vec(self.subtree, SubtreeRepr::read) }, - literal: read_vec(self.literal, LiteralRepr::read), + literal: if version >= EXTENDED_LEAF_DATA { + read_vec(self.literal, LiteralRepr::read_with_kind) + } else { + read_vec(self.literal, LiteralRepr::read) + }, punct: read_vec(self.punct, PunctRepr::read), - ident: read_vec(self.ident, IdentRepr::read), + ident: if version >= EXTENDED_LEAF_DATA { + read_vec(self.ident, IdentRepr::read_with_rawness) + } else { + read_vec(self.ident, IdentRepr::read) + }, token_tree: self.token_tree, text: self.text, span_data_table: &(), @@ -280,14 +315,20 @@ impl LiteralRepr { [self.id.0, self.text] } fn read([id, text]: [u32; 2]) -> LiteralRepr { - LiteralRepr { id: TokenId(id), text } + LiteralRepr { id: TokenId(id), text, kind: 0, suffix: !0 } + } + fn write_with_kind(self) -> [u32; 4] { + [self.id.0, self.text, self.kind as u32, self.suffix] + } + fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr { + LiteralRepr { id: TokenId(id), text, kind: kind as u16, suffix } } } impl PunctRepr { fn write(self) -> [u32; 3] { let spacing = match self.spacing { - tt::Spacing::Alone => 0, + tt::Spacing::Alone | tt::Spacing::JointHidden => 0, tt::Spacing::Joint => 1, }; [self.id.0, self.char as u32, spacing] @@ -307,7 +348,13 @@ impl IdentRepr { [self.id.0, self.text] } fn read(data: [u32; 2]) -> IdentRepr { - IdentRepr { id: TokenId(data[0]), text: data[1] } + IdentRepr { id: TokenId(data[0]), text: data[1], is_raw: false } + } + fn write_with_rawness(self) -> [u32; 3] { + [self.id.0, self.text, self.is_raw as u32] + } + fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr { + IdentRepr { id: TokenId(id), text, is_raw: is_raw == 1 } } } @@ -380,7 +427,25 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { let idx = self.literal.len() as u32; let text = self.intern(&lit.text); let id = self.token_id_of(lit.span); - self.literal.push(LiteralRepr { id, text }); + let suffix = lit.suffix.as_ref().map(|s| self.intern(s)).unwrap_or(!0); + self.literal.push(LiteralRepr { + id, + text, + kind: u16::from_le_bytes(match lit.kind { + tt::LitKind::Err(_) => [0, 0], + tt::LitKind::Byte => [1, 0], + tt::LitKind::Char => [2, 0], + tt::LitKind::Integer => [3, 0], + tt::LitKind::Float => [4, 0], + tt::LitKind::Str => [5, 0], + tt::LitKind::StrRaw(r) => [6, r], + tt::LitKind::ByteStr => [7, 0], + tt::LitKind::ByteStrRaw(r) => [8, r], + tt::LitKind::CStr => [9, 0], + tt::LitKind::CStrRaw(r) => [10, r], + }), + suffix, + }); idx << 2 | 0b01 } tt::Leaf::Punct(punct) => { @@ -393,7 +458,11 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { let idx = self.ident.len() as u32; let text = self.intern(&ident.text); let id = self.token_id_of(ident.span); - self.ident.push(IdentRepr { id, text }); + self.ident.push(IdentRepr { + id, + text, + is_raw: ident.is_raw == tt::IdentIsRaw::Yes, + }); idx << 2 | 0b11 } }, @@ -457,10 +526,32 @@ impl<'span, S: InternableSpan> Reader<'span, S> { // that this unwrap doesn't fire. 0b00 => res[idx].take().unwrap().into(), 0b01 => { + use tt::LitKind::*; let repr = &self.literal[idx]; tt::Leaf::Literal(tt::Literal { text: self.text[repr.text as usize].as_str().into(), span: read_span(repr.id), + kind: match u16::to_le_bytes(repr.kind) { + [0, _] => Err(()), + [1, _] => Byte, + [2, _] => Char, + [3, _] => Integer, + [4, _] => Float, + [5, _] => Str, + [6, r] => StrRaw(r), + [7, _] => ByteStr, + [8, r] => ByteStrRaw(r), + [9, _] => CStr, + [10, r] => CStrRaw(r), + _ => unreachable!(), + }, + suffix: if repr.suffix != !0 { + Some(Box::new( + self.text[repr.suffix as usize].as_str().into(), + )) + } else { + None + }, }) .into() } @@ -478,6 +569,11 @@ impl<'span, S: InternableSpan> Reader<'span, S> { tt::Leaf::Ident(tt::Ident { text: self.text[repr.text as usize].as_str().into(), span: read_span(repr.id), + is_raw: if repr.is_raw { + tt::IdentIsRaw::Yes + } else { + tt::IdentIsRaw::No + }, }) .into() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index a1707364f3c21..749a7760592b5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -1,6 +1,5 @@ //! Exports a few trivial procedural macros for testing. - #![feature(proc_macro_span, proc_macro_def_site)] #![allow(clippy::all)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs index e8b340a43d305..68e0f85978e0c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs @@ -49,58 +49,39 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { #[allow(unused)] fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing { match spacing { - Spacing::Alone => proc_macro::Spacing::Alone, + Spacing::Alone | Spacing::JointHidden => proc_macro::Spacing::Alone, Spacing::Joint => proc_macro::Spacing::Joint, } } -/// Invokes the callback with a `&[&str]` consisting of each part of the -/// literal's representation. This is done to allow the `ToString` and -/// `Display` implementations to borrow references to symbol values, and -/// both be optimized to reduce overhead. -fn literal_with_stringify_parts( - literal: &bridge::Literal, - interner: SymbolInternerRef, - f: impl FnOnce(&[&str]) -> R, -) -> R { - /// Returns a string containing exactly `num` '#' characters. - /// Uses a 256-character source string literal which is always safe to - /// index with a `u8` index. - fn get_hashes_str(num: u8) -> &'static str { - const HASHES: &str = "\ - ################################################################\ - ################################################################\ - ################################################################\ - ################################################################\ - "; - const _: () = assert!(HASHES.len() == 256); - &HASHES[..num as usize] +fn literal_kind_to_external(kind: tt::LitKind) -> bridge::LitKind { + match kind { + tt::LitKind::Byte => bridge::LitKind::Byte, + tt::LitKind::Char => bridge::LitKind::Char, + tt::LitKind::Integer => bridge::LitKind::Integer, + tt::LitKind::Float => bridge::LitKind::Float, + tt::LitKind::Str => bridge::LitKind::Str, + tt::LitKind::StrRaw(r) => bridge::LitKind::StrRaw(r), + tt::LitKind::ByteStr => bridge::LitKind::ByteStr, + tt::LitKind::ByteStrRaw(r) => bridge::LitKind::ByteStrRaw(r), + tt::LitKind::CStr => bridge::LitKind::CStr, + tt::LitKind::CStrRaw(r) => bridge::LitKind::CStrRaw(r), + tt::LitKind::Err(_) => bridge::LitKind::ErrWithGuar, } +} - { - let symbol = &*literal.symbol.text(interner); - let suffix = &*literal.suffix.map(|s| s.text(interner)).unwrap_or_default(); - match literal.kind { - bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]), - bridge::LitKind::Char => f(&["'", symbol, "'", suffix]), - bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]), - bridge::LitKind::StrRaw(n) => { - let hashes = get_hashes_str(n); - f(&["r", hashes, "\"", symbol, "\"", hashes, suffix]) - } - bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]), - bridge::LitKind::ByteStrRaw(n) => { - let hashes = get_hashes_str(n); - f(&["br", hashes, "\"", symbol, "\"", hashes, suffix]) - } - bridge::LitKind::CStr => f(&["c\"", symbol, "\"", suffix]), - bridge::LitKind::CStrRaw(n) => { - let hashes = get_hashes_str(n); - f(&["cr", hashes, "\"", symbol, "\"", hashes, suffix]) - } - bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::ErrWithGuar => { - f(&[symbol, suffix]) - } - } +fn literal_kind_to_internal(kind: bridge::LitKind) -> tt::LitKind { + match kind { + bridge::LitKind::Byte => tt::LitKind::Byte, + bridge::LitKind::Char => tt::LitKind::Char, + bridge::LitKind::Str => tt::LitKind::Str, + bridge::LitKind::StrRaw(r) => tt::LitKind::StrRaw(r), + bridge::LitKind::ByteStr => tt::LitKind::ByteStr, + bridge::LitKind::ByteStrRaw(r) => tt::LitKind::ByteStrRaw(r), + bridge::LitKind::CStr => tt::LitKind::CStr, + bridge::LitKind::CStrRaw(r) => tt::LitKind::CStrRaw(r), + bridge::LitKind::Integer => tt::LitKind::Integer, + bridge::LitKind::Float => tt::LitKind::Float, + bridge::LitKind::ErrWithGuar => tt::LitKind::Err(()), } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index bb174ba1b2243..1a71f39612d3b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -15,7 +15,7 @@ use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; use tt::{TextRange, TextSize}; use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_with_stringify_parts, + delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER, }; mod tt { @@ -171,20 +171,24 @@ impl server::TokenStream for RaSpanServer { bridge::TokenTree::Ident(ident) => { let text = ident.sym.text(self.interner); - let text = - if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; - let ident: tt::Ident = tt::Ident { text, span: ident.span }; + let ident: tt::Ident = tt::Ident { + text, + span: ident.span, + is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, + }; let leaf = tt::Leaf::from(ident); let tree = tt::TokenTree::from(leaf); Self::TokenStream::from_iter(iter::once(tree)) } bridge::TokenTree::Literal(literal) => { - let text = literal_with_stringify_parts(&literal, self.interner, |parts| { - ::tt::SmolStr::from_iter(parts.iter().copied()) - }); + let literal = tt::Literal { + text: literal.symbol.text(self.interner), + suffix: literal.suffix.map(|it| Box::new(it.text(self.interner))), + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; - let literal = tt::Literal { text, span: literal.span }; let leaf: tt::Leaf = tt::Leaf::from(literal); let tree = tt::TokenTree::from(leaf); Self::TokenStream::from_iter(iter::once(tree)) @@ -250,23 +254,18 @@ impl server::TokenStream for RaSpanServer { .into_iter() .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(match ident.text.strip_prefix("r#") { - Some(text) => bridge::Ident { - sym: Symbol::intern(self.interner, text), - is_raw: true, - span: ident.span, - }, - None => bridge::Ident { - sym: Symbol::intern(self.interner, &ident.text), - is_raw: false, - span: ident.span, - }, + bridge::TokenTree::Ident(bridge::Ident { + sym: Symbol::intern(self.interner, &ident.text), + is_raw: ident.is_raw.yes(), + span: ident.span, }) } tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { bridge::TokenTree::Literal(bridge::Literal { span: lit.span, - ..server::FreeFunctions::literal_from_str(self, &lit.text).unwrap() + kind: literal_kind_to_external(lit.kind), + symbol: Symbol::intern(self.interner, &lit.text), + suffix: lit.suffix.map(|it| Symbol::intern(self.interner, &it)), }) } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 12edacbe39dc6..94d5748b0870f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -8,7 +8,7 @@ use std::{ use proc_macro::bridge::{self, server}; use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_with_stringify_parts, + delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER, }; mod tt { @@ -25,10 +25,8 @@ mod tt { } type Group = tt::Subtree; type TokenTree = tt::TokenTree; -#[allow(unused)] type Punct = tt::Punct; type Spacing = tt::Spacing; -#[allow(unused)] type Literal = tt::Literal; type Span = tt::TokenId; type TokenStream = crate::server_impl::TokenStream; @@ -162,20 +160,23 @@ impl server::TokenStream for TokenIdServer { bridge::TokenTree::Ident(ident) => { let text = ident.sym.text(self.interner); - let text = - if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; - let ident: tt::Ident = tt::Ident { text, span: ident.span }; + let ident: tt::Ident = tt::Ident { + text, + span: ident.span, + is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, + }; let leaf = tt::Leaf::from(ident); let tree = TokenTree::from(leaf); Self::TokenStream::from_iter(iter::once(tree)) } bridge::TokenTree::Literal(literal) => { - let text = literal_with_stringify_parts(&literal, self.interner, |parts| { - ::tt::SmolStr::from_iter(parts.iter().copied()) - }); - - let literal = tt::Literal { text, span: literal.span }; + let literal = Literal { + text: literal.symbol.text(self.interner), + suffix: literal.suffix.map(|it| Box::new(it.text(self.interner))), + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; let leaf = tt::Leaf::from(literal); let tree = TokenTree::from(leaf); @@ -183,7 +184,7 @@ impl server::TokenStream for TokenIdServer { } bridge::TokenTree::Punct(p) => { - let punct = tt::Punct { + let punct = Punct { char: p.ch as char, spacing: if p.joint { Spacing::Joint } else { Spacing::Alone }, span: p.span, @@ -238,16 +239,17 @@ impl server::TokenStream for TokenIdServer { .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), - is_raw: ident.text.starts_with("r#"), + sym: Symbol::intern(self.interner, &ident.text), + is_raw: ident.is_raw.yes(), span: ident.span, }) } tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { bridge::TokenTree::Literal(bridge::Literal { span: lit.span, - ..server::FreeFunctions::literal_from_str(self, &lit.text) - .unwrap_or_else(|_| panic!("`{}`", lit.text)) + kind: literal_kind_to_external(lit.kind), + symbol: Symbol::intern(self.interner, &lit.text), + suffix: lit.suffix.map(|it| Symbol::intern(self.interner, &it)), }) } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { @@ -383,10 +385,12 @@ mod tests { tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: "struct".into(), span: tt::TokenId(0), + is_raw: tt::IdentIsRaw::No, })), tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: "T".into(), span: tt::TokenId(0), + is_raw: tt::IdentIsRaw::No, })), tt::TokenTree::Subtree(tt::Subtree { delimiter: tt::Delimiter { @@ -411,6 +415,7 @@ mod tests { kind: tt::DelimiterKind::Parenthesis, }, token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + is_raw: tt::IdentIsRaw::No, text: "a".into(), span: tt::TokenId(0), }))]), @@ -430,6 +435,7 @@ mod tests { tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text: "_".into(), span: tt::TokenId(0), + is_raw: tt::IdentIsRaw::No, })) ); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 6334282538089..dc6e71163b2f2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -21,20 +21,20 @@ fn test_derive_error() { assert_expand( "DeriveError", r#"struct S;"#, - expect![[r##" + expect![[r#" SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 SUBTREE () 1 1 - LITERAL "#[derive(DeriveError)] struct S ;"1 - PUNCH ; [alone] 1"##]], - expect![[r##" + LITERAL Str #[derive(DeriveError)] struct S ; 1 + PUNCH ; [alone] 1"#]], + expect![[r#" SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 IDENT compile_error 42:2@0..100#0 PUNCH ! [alone] 42:2@0..100#0 SUBTREE () 42:2@0..100#0 42:2@0..100#0 - LITERAL "#[derive(DeriveError)] struct S ;"42:2@0..100#0 - PUNCH ; [alone] 42:2@0..100#0"##]], + LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#0 + PUNCH ; [alone] 42:2@0..100#0"#]], ); } @@ -47,18 +47,18 @@ fn test_fn_like_macro_noop() { SUBTREE $$ 1 1 IDENT ident 1 PUNCH , [alone] 1 - LITERAL 01 + LITERAL Integer 0 1 PUNCH , [alone] 1 - LITERAL 11 + LITERAL Integer 1 1 PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 IDENT ident 42:2@0..5#0 PUNCH , [alone] 42:2@5..6#0 - LITERAL 042:2@7..8#0 + LITERAL Integer 0 42:2@7..8#0 PUNCH , [alone] 42:2@8..9#0 - LITERAL 142:2@10..11#0 + LITERAL Integer 1 42:2@10..11#0 PUNCH , [alone] 42:2@11..12#0 SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]], ); @@ -135,22 +135,22 @@ fn test_fn_like_mk_literals() { r#""#, expect![[r#" SUBTREE $$ 1 1 - LITERAL b"byte_string"1 - LITERAL 'c'1 - LITERAL "string"1 - LITERAL 3.14f641 - LITERAL 3.141 - LITERAL 123i641 - LITERAL 1231"#]], + LITERAL ByteStr byte_string 1 + LITERAL Char c 1 + LITERAL Str string 1 + LITERAL Float 3.14f64 1 + LITERAL Float 3.14 1 + LITERAL Integer 123i64 1 + LITERAL Integer 123 1"#]], expect![[r#" SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - LITERAL b"byte_string"42:2@0..100#0 - LITERAL 'c'42:2@0..100#0 - LITERAL "string"42:2@0..100#0 - LITERAL 3.14f6442:2@0..100#0 - LITERAL 3.1442:2@0..100#0 - LITERAL 123i6442:2@0..100#0 - LITERAL 12342:2@0..100#0"#]], + LITERAL ByteStr byte_string 42:2@0..100#0 + LITERAL Char c 42:2@0..100#0 + LITERAL Str string 42:2@0..100#0 + LITERAL Float 3.14f64 42:2@0..100#0 + LITERAL Float 3.14 42:2@0..100#0 + LITERAL Integer 123i64 42:2@0..100#0 + LITERAL Integer 123 42:2@0..100#0"#]], ); } @@ -175,50 +175,50 @@ fn test_fn_like_macro_clone_literals() { assert_expand( "fn_like_clone_tokens", r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###, - expect![[r###" + expect![[r#" SUBTREE $$ 1 1 - LITERAL 1u161 + LITERAL Integer 1u16 1 PUNCH , [alone] 1 - LITERAL 2_u321 + LITERAL Integer 2_u32 1 PUNCH , [alone] 1 PUNCH - [alone] 1 - LITERAL 4i641 + LITERAL Integer 4i64 1 PUNCH , [alone] 1 - LITERAL 3.14f321 + LITERAL Float 3.14f32 1 PUNCH , [alone] 1 - LITERAL "hello bridge"1 + LITERAL Str hello bridge 1 PUNCH , [alone] 1 - LITERAL "suffixed"suffix1 + LITERAL Str suffixedsuffix 1 PUNCH , [alone] 1 - LITERAL r##"raw"##1 + LITERAL StrRaw(2) raw 1 PUNCH , [alone] 1 - LITERAL 'a'1 + LITERAL Char a 1 PUNCH , [alone] 1 - LITERAL b'b'1 + LITERAL Byte b 1 PUNCH , [alone] 1 - LITERAL c"null"1"###]], - expect![[r###" + LITERAL CStr null 1"#]], + expect![[r#" SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - LITERAL 1u1642:2@0..4#0 + LITERAL Integer 1u16 42:2@0..4#0 PUNCH , [alone] 42:2@4..5#0 - LITERAL 2_u3242:2@6..11#0 + LITERAL Integer 2_u32 42:2@6..11#0 PUNCH , [alone] 42:2@11..12#0 PUNCH - [alone] 42:2@13..14#0 - LITERAL 4i6442:2@14..18#0 + LITERAL Integer 4i64 42:2@14..18#0 PUNCH , [alone] 42:2@18..19#0 - LITERAL 3.14f3242:2@20..27#0 + LITERAL Float 3.14f32 42:2@20..27#0 PUNCH , [alone] 42:2@27..28#0 - LITERAL "hello bridge"42:2@29..43#0 + LITERAL Str hello bridge 42:2@29..43#0 PUNCH , [alone] 42:2@43..44#0 - LITERAL "suffixed"suffix42:2@45..61#0 + LITERAL Str suffixedsuffix 42:2@45..61#0 PUNCH , [alone] 42:2@61..62#0 - LITERAL r##"raw"##42:2@63..73#0 + LITERAL StrRaw(2) raw 42:2@63..73#0 PUNCH , [alone] 42:2@73..74#0 - LITERAL 'a'42:2@75..78#0 + LITERAL Char a 42:2@75..78#0 PUNCH , [alone] 42:2@78..79#0 - LITERAL b'b'42:2@80..84#0 + LITERAL Byte b 42:2@80..84#0 PUNCH , [alone] 42:2@84..85#0 - LITERAL c"null"42:2@86..93#0"###]], + LITERAL CStr null 42:2@86..93#0"#]], ); } @@ -231,20 +231,20 @@ fn test_attr_macro() { "attr_error", r#"mod m {}"#, r#"some arguments"#, - expect![[r##" + expect![[r#" SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 SUBTREE () 1 1 - LITERAL "#[attr_error(some arguments)] mod m {}"1 - PUNCH ; [alone] 1"##]], - expect![[r##" + LITERAL Str #[attr_error(some arguments)] mod m {} 1 + PUNCH ; [alone] 1"#]], + expect![[r#" SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 IDENT compile_error 42:2@0..100#0 PUNCH ! [alone] 42:2@0..100#0 SUBTREE () 42:2@0..100#0 42:2@0..100#0 - LITERAL "#[attr_error(some arguments)] mod m {}"42:2@0..100#0 - PUNCH ; [alone] 42:2@0..100#0"##]], + LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#0 + PUNCH ; [alone] 42:2@0..100#0"#]], ); } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs b/src/tools/rust-analyzer/crates/salsa/tests/macros.rs index 3d818e53c8e93..9b07740e7de3d 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/salsa/tests/macros.rs @@ -5,6 +5,7 @@ trait MyDatabase: salsa::Database { } mod another_module { + #[allow(dead_code)] pub(crate) fn another_name(_: &dyn crate::MyDatabase, (): ()) {} } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 369744d0e96c7..24fd0abada5f2 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -12,8 +12,54 @@ use stdx::impl_from; pub use smol_str::SmolStr; pub use text_size::{TextRange, TextSize}; +#[derive(Clone, PartialEq, Debug)] +pub struct Lit { + pub kind: LitKind, + pub symbol: SmolStr, + pub suffix: Option, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum IdentIsRaw { + No, + Yes, +} +impl IdentIsRaw { + pub fn yes(self) -> bool { + matches!(self, IdentIsRaw::Yes) + } + pub fn as_str(self) -> &'static str { + match self { + IdentIsRaw::No => "", + IdentIsRaw::Yes => "r#", + } + } + pub fn split_from_symbol(sym: &str) -> (Self, &str) { + if let Some(sym) = sym.strip_prefix("r#") { + (IdentIsRaw::Yes, sym) + } else { + (IdentIsRaw::No, sym) + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum LitKind { + Byte, + Char, + Integer, // e.g. `1`, `1u8`, `1f32` + Float, // e.g. `1.`, `1.0`, `1e3f32` + Str, + StrRaw(u8), // raw string delimited by `n` hash symbols + ByteStr, + ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols + CStr, + CStrRaw(u8), + Err(()), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum TokenTree { +pub enum TokenTree { Leaf(Leaf), Subtree(Subtree), } @@ -103,6 +149,15 @@ pub struct DelimSpan { pub close: S, } +impl DelimSpan { + pub fn from_single(sp: Span) -> Self { + DelimSpan { open: sp, close: sp } + } + + pub fn from_pair(open: Span, close: Span) -> Self { + DelimSpan { open, close } + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Delimiter { pub open: S, @@ -134,8 +189,11 @@ pub enum DelimiterKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Literal { + // escaped pub text: SmolStr, pub span: S, + pub kind: LitKind, + pub suffix: Option>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -145,23 +203,85 @@ pub struct Punct { pub span: S, } +/// Indicates whether a token can join with the following token to form a +/// compound token. Used for conversions to `proc_macro::Spacing`. Also used to +/// guide pretty-printing, which is where the `JointHidden` value (which isn't +/// part of `proc_macro::Spacing`) comes in useful. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Spacing { + /// The token cannot join with the following token to form a compound + /// token. + /// + /// In token streams parsed from source code, the compiler will use `Alone` + /// for any token immediately followed by whitespace, a non-doc comment, or + /// EOF. + /// + /// When constructing token streams within the compiler, use this for each + /// token that (a) should be pretty-printed with a space after it, or (b) + /// is the last token in the stream. (In the latter case the choice of + /// spacing doesn't matter because it is never used for the last token. We + /// arbitrarily use `Alone`.) + /// + /// Converts to `proc_macro::Spacing::Alone`, and + /// `proc_macro::Spacing::Alone` converts back to this. Alone, - /// Whether the following token is joint to this one. + + /// The token can join with the following token to form a compound token. + /// + /// In token streams parsed from source code, the compiler will use `Joint` + /// for any token immediately followed by punctuation (as determined by + /// `Token::is_punct`). + /// + /// When constructing token streams within the compiler, use this for each + /// token that (a) should be pretty-printed without a space after it, and + /// (b) is followed by a punctuation token. + /// + /// Converts to `proc_macro::Spacing::Joint`, and + /// `proc_macro::Spacing::Joint` converts back to this. Joint, + + /// The token can join with the following token to form a compound token, + /// but this will not be visible at the proc macro level. (This is what the + /// `Hidden` means; see below.) + /// + /// In token streams parsed from source code, the compiler will use + /// `JointHidden` for any token immediately followed by anything not + /// covered by the `Alone` and `Joint` cases: an identifier, lifetime, + /// literal, delimiter, doc comment. + /// + /// When constructing token streams, use this for each token that (a) + /// should be pretty-printed without a space after it, and (b) is followed + /// by a non-punctuation token. + /// + /// Converts to `proc_macro::Spacing::Alone`, but + /// `proc_macro::Spacing::Alone` converts back to `token::Spacing::Alone`. + /// Because of that, pretty-printing of `TokenStream`s produced by proc + /// macros is unavoidably uglier (with more whitespace between tokens) than + /// pretty-printing of `TokenStream`'s produced by other means (i.e. parsed + /// source code, internally constructed token streams, and token streams + /// produced by declarative macros). + JointHidden, } +/// Identifier or keyword. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -/// Identifier or keyword. Unlike rustc, we keep "r#" prefix when it represents a raw identifier. pub struct Ident { pub text: SmolStr, pub span: S, + pub is_raw: IdentIsRaw, } impl Ident { - pub fn new(text: impl Into, span: S) -> Self { - Ident { text: text.into(), span } + pub fn new(text: impl Into + AsRef, span: S) -> Self { + let t = text.as_ref(); + // let raw_stripped = IdentIsRaw::split_from_symbol(text.as_ref()); + let raw_stripped = t.strip_prefix("r#"); + let is_raw = if raw_stripped.is_none() { IdentIsRaw::No } else { IdentIsRaw::Yes }; + let text = match raw_stripped { + Some(derawed) => derawed.into(), + None => text.into(), + }; + Ident { text, span, is_raw } } } @@ -207,22 +327,35 @@ fn print_debug_token( match tkn { TokenTree::Leaf(leaf) => match leaf { Leaf::Literal(lit) => { - write!(f, "{}LITERAL {}", align, lit.text)?; - fmt::Debug::fmt(&lit.span, f)?; + write!( + f, + "{}LITERAL {:?} {}{} {:#?}", + align, + lit.kind, + lit.text, + lit.suffix.as_ref().map(|it| &***it).unwrap_or(""), + lit.span + )?; } Leaf::Punct(punct) => { write!( f, - "{}PUNCH {} [{}] ", + "{}PUNCH {} [{}] {:#?}", align, punct.char, if punct.spacing == Spacing::Alone { "alone" } else { "joint" }, + punct.span )?; - fmt::Debug::fmt(&punct.span, f)?; } Leaf::Ident(ident) => { - write!(f, "{}IDENT {} ", align, ident.text)?; - fmt::Debug::fmt(&ident.span, f)?; + write!( + f, + "{}IDENT {}{} {:#?}", + align, + ident.is_raw.as_str(), + ident.text, + ident.span + )?; } }, TokenTree::Subtree(subtree) => { @@ -288,13 +421,52 @@ impl fmt::Display for Leaf { impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.is_raw.as_str(), f)?; fmt::Display::fmt(&self.text, f) } } impl fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.text, f) + match self.kind { + LitKind::Byte => write!(f, "b'{}'", self.text), + LitKind::Char => write!(f, "'{}'", self.text), + LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", self.text), + LitKind::Str => write!(f, "\"{}\"", self.text), + LitKind::ByteStr => write!(f, "b\"{}\"", self.text), + LitKind::CStr => write!(f, "c\"{}\"", self.text), + LitKind::StrRaw(num_of_hashes) => { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"r{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"br{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"cr{0:# Subtree { let s = match it { Leaf::Literal(it) => it.text.to_string(), Leaf::Punct(it) => it.char.to_string(), - Leaf::Ident(it) => it.text.to_string(), + Leaf::Ident(it) => format!("{}{}", it.is_raw.as_str(), it.text), }; match (it, last) { (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => { @@ -369,7 +541,9 @@ impl Subtree { pub fn pretty(tkns: &[TokenTree]) -> String { fn tokentree_to_text(tkn: &TokenTree) -> String { match tkn { - TokenTree::Leaf(Leaf::Ident(ident)) => ident.text.clone().into(), + TokenTree::Leaf(Leaf::Ident(ident)) => { + format!("{}{}", ident.is_raw.as_str(), ident.text) + } TokenTree::Leaf(Leaf::Literal(literal)) => literal.text.clone().into(), TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char), TokenTree::Subtree(subtree) => { From ed2b355872161fca4b666cf1c459210c428c57ee Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jul 2024 12:33:51 +0200 Subject: [PATCH 105/806] Add cargo xtask install proc-macro-server --- src/tools/rust-analyzer/crates/ide/src/hover.rs | 2 +- src/tools/rust-analyzer/xtask/src/flags.rs | 17 ++++++++++++++--- src/tools/rust-analyzer/xtask/src/install.rs | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 701374616ad67..2006baa30a8c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -240,7 +240,7 @@ fn hover_simple( .flatten() .unique_by(|&(def, _, _)| def) .map(|(def, macro_arm, node)| { - dbg!(hover_for_definition(sema, file_id, def, &node, macro_arm, config)) + hover_for_definition(sema, file_id, def, &node, macro_arm, config) }) .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { acc.actions.extend(actions); diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs index cf4a22d476faf..fd4291de9e1b2 100644 --- a/src/tools/rust-analyzer/xtask/src/flags.rs +++ b/src/tools/rust-analyzer/xtask/src/flags.rs @@ -2,7 +2,7 @@ use std::{fmt, str::FromStr}; -use crate::install::{ClientOpt, ServerOpt}; +use crate::install::{ClientOpt, ProcMacroServerOpt, ServerOpt}; xflags::xflags! { src "./src/flags.rs" @@ -23,6 +23,10 @@ xflags::xflags! { optional --mimalloc /// Use jemalloc allocator for server. optional --jemalloc + + /// Install the proc-macro server. + optional --proc-macro-server + /// build in release with debug info set to 2. optional --dev-rel } @@ -109,6 +113,7 @@ pub struct Install { pub client: bool, pub code_bin: Option, pub server: bool, + pub proc_macro_server: bool, pub mimalloc: bool, pub jemalloc: bool, pub dev_rel: bool, @@ -284,7 +289,7 @@ impl Malloc { impl Install { pub(crate) fn server(&self) -> Option { - if self.client && !self.server { + if !self.server { return None; } let malloc = if self.mimalloc { @@ -296,8 +301,14 @@ impl Install { }; Some(ServerOpt { malloc, dev_rel: self.dev_rel }) } + pub(crate) fn proc_macro_server(&self) -> Option { + if !self.proc_macro_server { + return None; + } + Some(ProcMacroServerOpt { dev_rel: self.dev_rel }) + } pub(crate) fn client(&self) -> Option { - if !self.client && self.server { + if !self.client { return None; } Some(ClientOpt { code_bin: self.code_bin.clone() }) diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index 72e612f9e1d6b..eb33d6f9be696 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -15,6 +15,9 @@ impl flags::Install { if let Some(server) = self.server() { install_server(sh, server).context("install server")?; } + if let Some(server) = self.proc_macro_server() { + install_proc_macro_server(sh, server).context("install proc-macro server")?; + } if let Some(client) = self.client() { install_client(sh, client).context("install client")?; } @@ -34,6 +37,10 @@ pub(crate) struct ServerOpt { pub(crate) dev_rel: bool, } +pub(crate) struct ProcMacroServerOpt { + pub(crate) dev_rel: bool, +} + fn fix_path_for_mac(sh: &Shell) -> anyhow::Result<()> { let mut vscode_path: Vec = { const COMMON_APP_PATH: &str = @@ -132,3 +139,11 @@ fn install_server(sh: &Shell, opts: ServerOpt) -> anyhow::Result<()> { cmd.run()?; Ok(()) } + +fn install_proc_macro_server(sh: &Shell, opts: ProcMacroServerOpt) -> anyhow::Result<()> { + let profile = if opts.dev_rel { "dev-rel" } else { "release" }; + + let cmd = cmd!(sh, "cargo +nightly install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features sysroot-abi"); + cmd.run()?; + Ok(()) +} From 39eb8c6bc415ba222dccb22a29d8730092a06ee1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jul 2024 12:55:05 +0200 Subject: [PATCH 106/806] Escape fetched env vars in env! expansion --- .../macro_expansion_tests/builtin_fn_macro.rs | 2 +- .../crates/hir-expand/src/builtin_fn_macro.rs | 39 +++++++++++-------- .../crates/hir-ty/src/tests/macros.rs | 2 +- .../rust-analyzer/tests/slow-tests/main.rs | 2 +- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index ab8bb8bd4c6fc..487ab537cdac3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -154,7 +154,7 @@ fn main() { file!(); } #[rustc_builtin_macro] macro_rules! file {() => {}} -fn main() { ""; } +fn main() { "file"; } "##]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 32befb7a7f7df..fb12adb87cb34 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -8,7 +8,10 @@ use intern::sym; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; -use syntax::unescape::{unescape_byte, unescape_char, unescape_unicode, Mode}; +use syntax::{ + format_smolstr, + unescape::{unescape_byte, unescape_char, unescape_unicode, Mode}, +}; use crate::{ db::ExpandDatabase, @@ -265,7 +268,7 @@ fn file_expand( ) -> ExpandResult { // FIXME: RA purposefully lacks knowledge of absolute file names // so just return "". - let file_name = ""; + let file_name = "file"; let expanded = quote! {span => #file_name @@ -275,34 +278,36 @@ fn file_expand( } fn format_args_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, + _db: &dyn ExpandDatabase, + _id: MacroCallId, tt: &tt::Subtree, span: Span, ) -> ExpandResult { - format_args_expand_general(db, id, tt, "", span) + let pound = mk_pound(span); + let mut tt = tt.clone(); + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + ExpandResult::ok(quote! {span => + builtin #pound format_args #tt + }) } fn format_args_nl_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - tt: &tt::Subtree, - span: Span, -) -> ExpandResult { - format_args_expand_general(db, id, tt, "\\n", span) -} - -fn format_args_expand_general( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, - // FIXME: Make use of this so that mir interpretation works properly - _end_string: &str, span: Span, ) -> ExpandResult { let pound = mk_pound(span); let mut tt = tt.clone(); tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text, + kind: tt::LitKind::Str, + .. + }))) = tt.token_trees.first_mut() + { + *text = format_smolstr!("{text}\\n"); + } ExpandResult::ok(quote! {span => builtin #pound format_args #tt }) @@ -788,7 +793,7 @@ fn include_str_expand( fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; - db.crate_graph()[krate].env.get(key) + db.crate_graph()[krate].env.get(key).map(|it| it.escape_debug().to_string()) } fn env_expand( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index a0899cb1d632b..5454a496ba8c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -703,7 +703,7 @@ fn infer_builtin_macros_file() { } "#, expect![[r#" - !0..2 '""': &'static str + !0..6 '"file"': &'static str 63..87 '{ ...!(); }': () 73..74 'x': &'static str "#]], diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 56f416a0b6e04..aa17b587e0228 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -970,7 +970,7 @@ version = \"0.0.0\" fn out_dirs_check_impl(root_contains_symlink: bool) { if skip_slow_tests() { - return; + // return; } let mut server = Project::with_fixture( From 311aaa5a792489a1f80cfa0e522e0e60b08788de Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jul 2024 14:41:35 +0200 Subject: [PATCH 107/806] Fix incorrect encoding of literals in the proc-macro-api on version 4 --- src/tools/rust-analyzer/Cargo.lock | 3 +- .../crates/hir-expand/src/attrs.rs | 7 +- .../crates/hir-expand/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 3 +- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 9 +- .../crates/mbe/src/syntax_bridge.rs | 53 +------- .../crates/proc-macro-api/Cargo.toml | 2 + .../crates/proc-macro-api/src/msg.rs | 53 ++++---- .../crates/proc-macro-api/src/msg/flat.rs | 118 ++++++++++++------ .../crates/proc-macro-srv/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 4 + src/tools/rust-analyzer/crates/tt/src/lib.rs | 61 ++++++++- 12 files changed, 183 insertions(+), 134 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e43f712a6e230..500a150b57b84 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1046,7 +1046,6 @@ dependencies = [ "arrayvec", "cov-mark", "parser", - "ra-ap-rustc_lexer", "rustc-hash", "smallvec", "span", @@ -1326,6 +1325,7 @@ dependencies = [ "base-db", "indexmap", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mbe", "paths", "rustc-hash", "serde", @@ -2218,6 +2218,7 @@ name = "tt" version = "0.0.0" dependencies = [ "arrayvec", + "ra-ap-rustc_lexer", "smol_str", "stdx", "text-size", diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 4fce7c1fde13f..49a104fa118e6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -5,9 +5,10 @@ use base_db::CrateId; use cfg::CfgExpr; use either::Either; use intern::{sym, Interned}; + use mbe::{ - desugar_doc_comment_text, syntax_node_to_token_tree, token_to_literal, DelimiterKind, - DocCommentDesugarMode, Punct, + desugar_doc_comment_text, syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, + Punct, }; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; @@ -20,7 +21,7 @@ use crate::{ db::ExpandDatabase, mod_path::ModPath, span_map::SpanMapRef, - tt::{self, Subtree}, + tt::{self, token_to_literal, Subtree}, InFile, }; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index c4921da61004c..3460d1ca3d11f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -59,7 +59,7 @@ pub use span::{HirFileId, MacroCallId, MacroFileId}; pub mod tt { pub use span::Span; - pub use tt::{DelimiterKind, IdentIsRaw, LitKind, Spacing}; + pub use tt::{token_to_literal, DelimiterKind, IdentIsRaw, LitKind, Spacing}; pub type Delimiter = ::tt::Delimiter; pub type DelimSpan = ::tt::DelimSpan; diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 7ce8aadfb3619..1002de2104a0c 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -17,7 +17,6 @@ rustc-hash.workspace = true smallvec.workspace = true tracing.workspace = true arrayvec.workspace = true -ra-ap-rustc_lexer.workspace = true # local deps syntax.workspace = true @@ -30,7 +29,7 @@ span.workspace = true test-utils.workspace = true [features] -in-rust-tree = ["parser/in-rust-tree", "syntax/in-rust-tree"] +in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 8ab9269e9523a..44b056a1acf5f 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -6,13 +6,6 @@ //! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(not(feature = "in-rust-tree"))] -extern crate ra_ap_rustc_lexer as rustc_lexer; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_lexer; - mod expander; mod parser; mod syntax_bridge; @@ -36,7 +29,7 @@ pub use tt::{Delimiter, DelimiterKind, Punct}; pub use crate::syntax_bridge::{ desugar_doc_comment_text, parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified, - token_to_literal, token_tree_to_syntax_node, DocCommentDesugarMode, SpanMapper, + token_tree_to_syntax_node, DocCommentDesugarMode, SpanMapper, }; pub use crate::syntax_bridge::dummy_test_span_utils::*; diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 3feddba21061f..4d66464932b88 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, SpanAnchor, SpanData, SpanMap}; -use stdx::{format_to, itertools::Itertools, never, non_empty_vec::NonEmptyVec}; +use stdx::{format_to, never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, @@ -14,6 +14,7 @@ use syntax::{ use tt::{ buffer::{Cursor, TokenBuffer}, iter::TtIter, + token_to_literal, }; use crate::to_parser_input::to_parser_input; @@ -400,56 +401,6 @@ where } } -pub fn token_to_literal(text: SmolStr, span: S) -> tt::Literal -where - S: Copy, -{ - use rustc_lexer::LiteralKind; - - let token = rustc_lexer::tokenize(&text).next_tuple(); - let Some((rustc_lexer::Token { - kind: rustc_lexer::TokenKind::Literal { kind, suffix_start }, - .. - },)) = token - else { - return tt::Literal { span, text, kind: tt::LitKind::Err(()), suffix: None }; - }; - - let (kind, start_offset, end_offset) = match kind { - LiteralKind::Int { .. } => (tt::LitKind::Integer, 0, 0), - LiteralKind::Float { .. } => (tt::LitKind::Float, 0, 0), - LiteralKind::Char { terminated } => (tt::LitKind::Char, 1, terminated as usize), - LiteralKind::Byte { terminated } => (tt::LitKind::Byte, 2, terminated as usize), - LiteralKind::Str { terminated } => (tt::LitKind::Str, 1, terminated as usize), - LiteralKind::ByteStr { terminated } => (tt::LitKind::ByteStr, 2, terminated as usize), - LiteralKind::CStr { terminated } => (tt::LitKind::CStr, 2, terminated as usize), - LiteralKind::RawStr { n_hashes } => ( - tt::LitKind::StrRaw(n_hashes.unwrap_or_default()), - 2 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - LiteralKind::RawByteStr { n_hashes } => ( - tt::LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), - 3 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - LiteralKind::RawCStr { n_hashes } => ( - tt::LitKind::CStrRaw(n_hashes.unwrap_or_default()), - 3 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - }; - - let (lit, suffix) = text.split_at(suffix_start as usize); - let lit = &lit[start_offset..lit.len() - end_offset]; - let suffix = match suffix { - "" | "_" => None, - suffix => Some(Box::new(suffix.into())), - }; - - tt::Literal { span, text: lit.into(), kind, suffix } -} - fn is_single_token_op(kind: SyntaxKind) -> bool { matches!( kind, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 7f633d91ecc8c..889eefa8b5c61 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -28,6 +28,8 @@ span.workspace = true # InternIds for the syntax context base-db.workspace = true la-arena.workspace = true +# only here to parse via token_to_literal +mbe.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index b5f3d0c3aacd5..65835048173ab 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -197,7 +197,7 @@ mod tests { .into(), ), TokenTree::Leaf(Leaf::Literal(Literal { - text: "\"Foo\"".into(), + text: "Foo".into(), span: Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, @@ -263,32 +263,35 @@ mod tests { #[test] fn test_proc_macro_rpc_works() { let tt = fixture_token_tree(); - let mut span_data_table = Default::default(); - let task = ExpandMacro { - data: ExpandMacroData { - macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table), - macro_name: Default::default(), - attributes: None, - has_global_spans: ExpnGlobals { - serialize: true, - def_site: 0, - call_site: 0, - mixed_site: 0, + for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION { + let mut span_data_table = Default::default(); + let task = ExpandMacro { + data: ExpandMacroData { + macro_body: FlatTree::new(&tt, v, &mut span_data_table), + macro_name: Default::default(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: true, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: Vec::new(), }, - span_data_table: Vec::new(), - }, - lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(), - env: Default::default(), - current_dir: Default::default(), - }; + lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(), + env: Default::default(), + current_dir: Default::default(), + }; - let json = serde_json::to_string(&task).unwrap(); - // println!("{}", json); - let back: ExpandMacro = serde_json::from_str(&json).unwrap(); + let json = serde_json::to_string(&task).unwrap(); + // println!("{}", json); + let back: ExpandMacro = serde_json::from_str(&json).unwrap(); - assert_eq!( - tt, - back.data.macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) - ); + assert_eq!( + tt, + back.data.macro_body.to_subtree_resolved(v, &span_data_table), + "version: {v}" + ); + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index 7f5afdb7270fe..3d962e99d92c2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -141,6 +141,7 @@ impl FlatTree { ident: Vec::new(), token_tree: Vec::new(), text: Vec::new(), + version, }; w.write(subtree); @@ -178,6 +179,7 @@ impl FlatTree { ident: Vec::new(), token_tree: Vec::new(), text: Vec::new(), + version, }; w.write(subtree); @@ -228,6 +230,7 @@ impl FlatTree { token_tree: self.token_tree, text: self.text, span_data_table, + version, } .read() } @@ -253,6 +256,7 @@ impl FlatTree { token_tree: self.token_tree, text: self.text, span_data_table: &(), + version, } .read() } @@ -386,8 +390,9 @@ impl InternableSpan for Span { struct Writer<'a, 'span, S: InternableSpan> { work: VecDeque<(usize, &'a tt::Subtree)>, - string_table: FxHashMap<&'a str, u32>, + string_table: FxHashMap, u32>, span_data_table: &'span mut S::Table, + version: u32, subtree: Vec, literal: Vec, @@ -425,9 +430,15 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { tt::TokenTree::Leaf(leaf) => match leaf { tt::Leaf::Literal(lit) => { let idx = self.literal.len() as u32; - let text = self.intern(&lit.text); let id = self.token_id_of(lit.span); - let suffix = lit.suffix.as_ref().map(|s| self.intern(s)).unwrap_or(!0); + let (text, suffix) = if self.version >= EXTENDED_LEAF_DATA { + ( + self.intern(&lit.text), + lit.suffix.as_ref().map(|s| self.intern(s)).unwrap_or(!0), + ) + } else { + (self.intern_owned(format!("{lit}")), !0) + }; self.literal.push(LiteralRepr { id, text, @@ -456,13 +467,15 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { } tt::Leaf::Ident(ident) => { let idx = self.ident.len() as u32; - let text = self.intern(&ident.text); let id = self.token_id_of(ident.span); - self.ident.push(IdentRepr { - id, - text, - is_raw: ident.is_raw == tt::IdentIsRaw::Yes, - }); + let text = if self.version >= EXTENDED_LEAF_DATA { + self.intern(&ident.text) + } else if ident.is_raw.yes() { + self.intern_owned(format!("r#{}", ident.text,)) + } else { + self.intern(&ident.text) + }; + self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() }); idx << 2 | 0b11 } }, @@ -484,15 +497,25 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { pub(crate) fn intern(&mut self, text: &'a str) -> u32 { let table = &mut self.text; - *self.string_table.entry(text).or_insert_with(|| { + *self.string_table.entry(text.into()).or_insert_with(|| { let idx = table.len(); table.push(text.to_owned()); idx as u32 }) } + + pub(crate) fn intern_owned(&mut self, text: String) -> u32 { + let table = &mut self.text; + *self.string_table.entry(text.clone().into()).or_insert_with(|| { + let idx = table.len(); + table.push(text); + idx as u32 + }) + } } struct Reader<'span, S: InternableSpan> { + version: u32, subtree: Vec, literal: Vec, punct: Vec, @@ -528,30 +551,36 @@ impl<'span, S: InternableSpan> Reader<'span, S> { 0b01 => { use tt::LitKind::*; let repr = &self.literal[idx]; - tt::Leaf::Literal(tt::Literal { - text: self.text[repr.text as usize].as_str().into(), - span: read_span(repr.id), - kind: match u16::to_le_bytes(repr.kind) { - [0, _] => Err(()), - [1, _] => Byte, - [2, _] => Char, - [3, _] => Integer, - [4, _] => Float, - [5, _] => Str, - [6, r] => StrRaw(r), - [7, _] => ByteStr, - [8, r] => ByteStrRaw(r), - [9, _] => CStr, - [10, r] => CStrRaw(r), - _ => unreachable!(), - }, - suffix: if repr.suffix != !0 { - Some(Box::new( - self.text[repr.suffix as usize].as_str().into(), - )) - } else { - None - }, + let text = self.text[repr.text as usize].as_str(); + let span = read_span(repr.id); + tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { + tt::Literal { + text: text.into(), + span, + kind: match u16::to_le_bytes(repr.kind) { + [0, _] => Err(()), + [1, _] => Byte, + [2, _] => Char, + [3, _] => Integer, + [4, _] => Float, + [5, _] => Str, + [6, r] => StrRaw(r), + [7, _] => ByteStr, + [8, r] => ByteStrRaw(r), + [9, _] => CStr, + [10, r] => CStrRaw(r), + _ => unreachable!(), + }, + suffix: if repr.suffix != !0 { + Some(Box::new( + self.text[repr.suffix as usize].as_str().into(), + )) + } else { + None + }, + } + } else { + tt::token_to_literal(text.into(), span) }) .into() } @@ -566,14 +595,23 @@ impl<'span, S: InternableSpan> Reader<'span, S> { } 0b11 => { let repr = &self.ident[idx]; + let text = self.text[repr.text as usize].as_str(); + let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA { + ( + if repr.is_raw { + tt::IdentIsRaw::Yes + } else { + tt::IdentIsRaw::No + }, + text, + ) + } else { + tt::IdentIsRaw::split_from_symbol(text) + }; tt::Leaf::Ident(tt::Ident { - text: self.text[repr.text as usize].as_str().into(), + text: text.into(), span: read_span(repr.id), - is_raw: if repr.is_raw { - tt::IdentIsRaw::Yes - } else { - tt::IdentIsRaw::No - }, + is_raw, }) .into() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 735f781c43910..065701c05cc7b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -34,7 +34,7 @@ proc-macro-test.path = "./proc-macro-test" [features] sysroot-abi = [] -in-rust-tree = ["mbe/in-rust-tree", "sysroot-abi"] +in-rust-tree = ["mbe/in-rust-tree", "tt/in-rust-tree","sysroot-abi"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 1311e2ddf89e9..1900635b995ee 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -17,6 +17,10 @@ smol_str.workspace = true text-size.workspace = true stdx.workspace = true +ra-ap-rustc_lexer.workspace = true + +[features] +in-rust-tree = [] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 24fd0abada5f2..c328b3f8a3cc8 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -2,14 +2,21 @@ //! input and output) of macros. It closely mirrors `proc_macro` crate's //! `TokenTree`. +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + pub mod buffer; pub mod iter; use std::fmt; -use stdx::impl_from; +use stdx::{impl_from, itertools::Itertools as _}; -pub use smol_str::SmolStr; +pub use smol_str::{format_smolstr, SmolStr}; pub use text_size::{TextRange, TextSize}; #[derive(Clone, PartialEq, Debug)] @@ -196,6 +203,56 @@ pub struct Literal { pub suffix: Option>, } +pub fn token_to_literal(text: SmolStr, span: S) -> Literal +where + S: Copy, +{ + use rustc_lexer::LiteralKind; + + let token = rustc_lexer::tokenize(&text).next_tuple(); + let Some((rustc_lexer::Token { + kind: rustc_lexer::TokenKind::Literal { kind, suffix_start }, + .. + },)) = token + else { + return Literal { span, text, kind: LitKind::Err(()), suffix: None }; + }; + + let (kind, start_offset, end_offset) = match kind { + LiteralKind::Int { .. } => (LitKind::Integer, 0, 0), + LiteralKind::Float { .. } => (LitKind::Float, 0, 0), + LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize), + LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize), + LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize), + LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize), + LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize), + LiteralKind::RawStr { n_hashes } => ( + LitKind::StrRaw(n_hashes.unwrap_or_default()), + 2 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawByteStr { n_hashes } => ( + LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawCStr { n_hashes } => ( + LitKind::CStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + }; + + let (lit, suffix) = text.split_at(suffix_start as usize); + let lit = &lit[start_offset..lit.len() - end_offset]; + let suffix = match suffix { + "" | "_" => None, + suffix => Some(Box::new(suffix.into())), + }; + + Literal { span, text: lit.into(), kind, suffix } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Punct { pub char: char, From 4264548283dbaabb854e903994e4055215a956b8 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 15 Jul 2024 21:16:17 +0700 Subject: [PATCH 108/806] Add regression test for issue 13077 --- tests/ui/needless_option_as_deref.fixed | 14 ++++++++++++++ tests/ui/needless_option_as_deref.rs | 14 ++++++++++++++ tests/ui/needless_option_as_deref.stderr | 8 +++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index 58f56eba0f539..7bde155582bfd 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -52,3 +52,17 @@ struct S<'a> { fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { s.opt.as_deref_mut() } + +mod issue_non_copy_13077 { + pub fn something(mut maybe_side_effect: Option<&mut String>) { + for _ in 0..10 { + let _ = S { field: other(maybe_side_effect) }; + } + } + + fn other(_maybe_side_effect: Option<&mut String>) { + unimplemented!() + } + + pub struct S { pub field: () } +} diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index 842e025f669b9..ab06a38f742e4 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -52,3 +52,17 @@ struct S<'a> { fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { s.opt.as_deref_mut() } + +mod issue_non_copy_13077 { + pub fn something(mut maybe_side_effect: Option<&mut String>) { + for _ in 0..10 { + let _ = S { field: other(maybe_side_effect.as_deref_mut()) }; + } + } + + fn other(_maybe_side_effect: Option<&mut String>) { + unimplemented!() + } + + pub struct S { pub field: () } +} diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr index a05d0aa9276ac..3211062af9131 100644 --- a/tests/ui/needless_option_as_deref.stderr +++ b/tests/ui/needless_option_as_deref.stderr @@ -19,5 +19,11 @@ error: derefed type is same as origin LL | let _ = x.as_deref_mut(); | ^^^^^^^^^^^^^^^^ help: try: `x` -error: aborting due to 3 previous errors +error: derefed type is same as origin + --> tests/ui/needless_option_as_deref.rs:59:38 + | +LL | let _ = S { field: other(maybe_side_effect.as_deref_mut()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_side_effect` + +error: aborting due to 4 previous errors From dcee2e8a0fb95887284b7779f2fd809f148722c5 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 15 Jul 2024 15:56:17 +0000 Subject: [PATCH 109/806] Dont stop find loop node at struct field node --- clippy_utils/src/lib.rs | 2 +- tests/ui/needless_option_as_deref.fixed | 8 ++++++-- tests/ui/needless_option_as_deref.rs | 8 ++++++-- tests/ui/needless_option_as_deref.stderr | 8 +------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8a6750c899765..0156e223c8d49 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1407,7 +1407,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e), _ => (), }, - Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) => (), + Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (), _ => break, } } diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index 7bde155582bfd..84eaf12fc1398 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -56,7 +56,9 @@ fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { mod issue_non_copy_13077 { pub fn something(mut maybe_side_effect: Option<&mut String>) { for _ in 0..10 { - let _ = S { field: other(maybe_side_effect) }; + let _ = S { + field: other(maybe_side_effect.as_deref_mut()), + }; } } @@ -64,5 +66,7 @@ mod issue_non_copy_13077 { unimplemented!() } - pub struct S { pub field: () } + pub struct S { + pub field: (), + } } diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index ab06a38f742e4..fff1e45d846fb 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -56,7 +56,9 @@ fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { mod issue_non_copy_13077 { pub fn something(mut maybe_side_effect: Option<&mut String>) { for _ in 0..10 { - let _ = S { field: other(maybe_side_effect.as_deref_mut()) }; + let _ = S { + field: other(maybe_side_effect.as_deref_mut()), + }; } } @@ -64,5 +66,7 @@ mod issue_non_copy_13077 { unimplemented!() } - pub struct S { pub field: () } + pub struct S { + pub field: (), + } } diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr index 3211062af9131..a05d0aa9276ac 100644 --- a/tests/ui/needless_option_as_deref.stderr +++ b/tests/ui/needless_option_as_deref.stderr @@ -19,11 +19,5 @@ error: derefed type is same as origin LL | let _ = x.as_deref_mut(); | ^^^^^^^^^^^^^^^^ help: try: `x` -error: derefed type is same as origin - --> tests/ui/needless_option_as_deref.rs:59:38 - | -LL | let _ = S { field: other(maybe_side_effect.as_deref_mut()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_side_effect` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 8edf9b808427dfbe4425b49d213351f907f58b40 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 20 Mar 2024 20:30:04 -0400 Subject: [PATCH 110/806] Item bounds can reference self projections and still be object safe --- .../src/traits/object_safety.rs | 124 +++++++++++------- .../item-bounds-can-reference-self.rs | 11 ++ 2 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 tests/ui/object-safety/item-bounds-can-reference-self.rs diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 1c6993bdd3729..29c18512e6d98 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -12,16 +12,16 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, Obligation, ObligationCause}; +use crate::traits::{Obligation, ObligationCause}; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; +use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; -use rustc_middle::ty::{GenericArg, GenericArgs}; use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -195,7 +195,13 @@ fn predicates_reference_self( .predicates .iter() .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp)) - .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .filter_map(|(clause, sp)| { + // Super predicates cannot allow self projections, since they're + // impossible to make into existential bounds without eager resolution + // or something. + // e.g. `trait A: B`. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No) + }) .collect() } @@ -204,20 +210,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) - .filter_map(|c| predicate_references_self(tcx, c)) + .filter_map(|(clause, sp)| { + // Item bounds *can* have self projections, since they never get + // their self type erased. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes) + }) .collect() } fn predicate_references_self<'tcx>( tcx: TyCtxt<'tcx>, - (predicate, sp): (ty::Clause<'tcx>, Span), + trait_def_id: DefId, + predicate: ty::Clause<'tcx>, + sp: Span, + allow_self_projections: AllowSelfProjections, ) -> Option { - let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into()); match predicate.kind().skip_binder() { ty::ClauseKind::Trait(ref data) => { // In the case of a trait predicate, we can skip the "self" type. - data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp) + data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } ty::ClauseKind::Projection(ref data) => { // And similarly for projections. This should be redundant with @@ -235,9 +246,9 @@ fn predicate_references_self<'tcx>( // // This is ALT2 in issue #56288, see that for discussion of the // possible alternatives. - data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp) + data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } - ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp), + ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp), ty::ClauseKind::WellFormed(..) | ty::ClauseKind::TypeOutlives(..) @@ -383,7 +394,12 @@ fn virtual_call_violations_for_method<'tcx>( let mut errors = Vec::new(); for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.rebind(input_ty), + AllowSelfProjections::Yes, + ) { let span = if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, _), .. @@ -396,7 +412,12 @@ fn virtual_call_violations_for_method<'tcx>( errors.push(MethodViolationCode::ReferencesSelfInput(span)); } } - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.output(), + AllowSelfProjections::Yes, + ) { errors.push(MethodViolationCode::ReferencesSelfOutput); } if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { @@ -482,7 +503,7 @@ fn virtual_call_violations_for_method<'tcx>( return false; } - contains_illegal_self_type_reference(tcx, trait_def_id, pred) + contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes) }) { errors.push(MethodViolationCode::WhereClauseReferencesSelf); } @@ -711,10 +732,17 @@ fn receiver_is_dispatchable<'tcx>( infcx.predicate_must_hold_modulo_regions(&obligation) } +#[derive(Copy, Clone)] +enum AllowSelfProjections { + Yes, + No, +} + fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, + allow_self_projections: AllowSelfProjections, ) -> bool { // This is somewhat subtle. In general, we want to forbid // references to `Self` in the argument and return types, @@ -759,6 +787,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, supertraits: Option>, + allow_self_projections: AllowSelfProjections, } impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { @@ -780,39 +809,41 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( ControlFlow::Continue(()) } ty::Alias(ty::Projection, ref data) => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - let trait_ref = - ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)); - self.supertraits = Some( - traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(), - ); - } + match self.allow_self_projections { + AllowSelfProjections::Yes => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + self.supertraits = + Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect()); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = self - .supertraits - .as_ref() - .unwrap() - .contains(&data.trait_ref(self.tcx).def_id); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { - ControlFlow::Continue(()) - } else { - t.super_visit_with(self) // POSSIBLY reporting an error + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = self + .supertraits + .as_ref() + .unwrap() + .contains(&data.trait_ref(self.tcx).def_id); + + // only walk contained types if it's not a super trait + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) + } else { + t.super_visit_with(self) // POSSIBLY reporting an error + } + } + AllowSelfProjections::No => t.super_visit_with(self), } } - _ => t.super_visit_with(self), // walk contained types, if any + _ => t.super_visit_with(self), } } @@ -824,7 +855,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( } value - .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) + .visit_with(&mut IllegalSelfTypeVisitor { + tcx, + trait_def_id, + supertraits: None, + allow_self_projections, + }) .is_break() } diff --git a/tests/ui/object-safety/item-bounds-can-reference-self.rs b/tests/ui/object-safety/item-bounds-can-reference-self.rs new file mode 100644 index 0000000000000..4ae982e8f951f --- /dev/null +++ b/tests/ui/object-safety/item-bounds-can-reference-self.rs @@ -0,0 +1,11 @@ +//@ check-pass + +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} + +fn uwu(x: &dyn Foo) {} + +fn main() {} From 172cf9bef3411d479a0be98827647150cce48afd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 6 Jun 2024 14:40:56 -0400 Subject: [PATCH 111/806] Fix unsoundness when associated types dont actually come from supertraits --- .../src/traits/object_safety.rs | 264 ++++++++++-------- .../almost-supertrait-associated-type.rs | 60 ++++ .../almost-supertrait-associated-type.stderr | 55 ++++ 3 files changed, 269 insertions(+), 110 deletions(-) create mode 100644 tests/ui/object-safety/almost-supertrait-associated-type.rs create mode 100644 tests/ui/object-safety/almost-supertrait-associated-type.stderr diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 29c18512e6d98..ec19cf2766816 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -12,17 +12,16 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{Obligation, ObligationCause}; +use crate::traits::{util, Obligation, ObligationCause}; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; -use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ - self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, + self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, }; -use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::abi::Abi; @@ -738,130 +737,175 @@ enum AllowSelfProjections { No, } +/// This is somewhat subtle. In general, we want to forbid +/// references to `Self` in the argument and return types, +/// since the value of `Self` is erased. However, there is one +/// exception: it is ok to reference `Self` in order to access +/// an associated type of the current trait, since we retain +/// the value of those associated types in the object type +/// itself. +/// +/// ```rust,ignore (example) +/// trait SuperTrait { +/// type X; +/// } +/// +/// trait Trait : SuperTrait { +/// type Y; +/// fn foo(&self, x: Self) // bad +/// fn foo(&self) -> Self // bad +/// fn foo(&self) -> Option // bad +/// fn foo(&self) -> Self::Y // OK, desugars to next example +/// fn foo(&self) -> ::Y // OK +/// fn foo(&self) -> Self::X // OK, desugars to next example +/// fn foo(&self) -> ::X // OK +/// } +/// ``` +/// +/// However, it is not as simple as allowing `Self` in a projected +/// type, because there are illegal ways to use `Self` as well: +/// +/// ```rust,ignore (example) +/// trait Trait : SuperTrait { +/// ... +/// fn foo(&self) -> ::X; +/// } +/// ``` +/// +/// Here we will not have the type of `X` recorded in the +/// object type, and we cannot resolve `Self as SomeOtherTrait` +/// without knowing what `Self` is. fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, allow_self_projections: AllowSelfProjections, ) -> bool { - // This is somewhat subtle. In general, we want to forbid - // references to `Self` in the argument and return types, - // since the value of `Self` is erased. However, there is one - // exception: it is ok to reference `Self` in order to access - // an associated type of the current trait, since we retain - // the value of those associated types in the object type - // itself. - // - // ```rust - // trait SuperTrait { - // type X; - // } - // - // trait Trait : SuperTrait { - // type Y; - // fn foo(&self, x: Self) // bad - // fn foo(&self) -> Self // bad - // fn foo(&self) -> Option // bad - // fn foo(&self) -> Self::Y // OK, desugars to next example - // fn foo(&self) -> ::Y // OK - // fn foo(&self) -> Self::X // OK, desugars to next example - // fn foo(&self) -> ::X // OK - // } - // ``` - // - // However, it is not as simple as allowing `Self` in a projected - // type, because there are illegal ways to use `Self` as well: - // - // ```rust - // trait Trait : SuperTrait { - // ... - // fn foo(&self) -> ::X; - // } - // ``` - // - // Here we will not have the type of `X` recorded in the - // object type, and we cannot resolve `Self as SomeOtherTrait` - // without knowing what `Self` is. - - struct IllegalSelfTypeVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - supertraits: Option>, - allow_self_projections: AllowSelfProjections, - } + value + .visit_with(&mut IllegalSelfTypeVisitor { + tcx, + trait_def_id, + supertraits: None, + allow_self_projections, + }) + .is_break() +} - impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { - type Result = ControlFlow<()>; +struct IllegalSelfTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + supertraits: Option>>, + allow_self_projections: AllowSelfProjections, +} - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match t.kind() { - ty::Param(_) => { - if t == self.tcx.types.self_param { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } - ty::Alias(ty::Projection, ref data) - if self.tcx.is_impl_trait_in_trait(data.def_id) => - { - // We'll deny these later in their own pass +impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Param(_) => { + if t == self.tcx.types.self_param { + ControlFlow::Break(()) + } else { ControlFlow::Continue(()) } - ty::Alias(ty::Projection, ref data) => { - match self.allow_self_projections { - AllowSelfProjections::Yes => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - self.supertraits = - Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect()); - } + } + ty::Alias(ty::Projection, ref data) if self.tcx.is_impl_trait_in_trait(data.def_id) => { + // We'll deny these later in their own pass + ControlFlow::Continue(()) + } + ty::Alias(ty::Projection, ref data) => { + match self.allow_self_projections { + AllowSelfProjections::Yes => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + self.supertraits = Some( + util::supertraits( + self.tcx, + ty::Binder::dummy(ty::TraitRef::identity( + self.tcx, + self.trait_def_id, + )), + ) + .map(|trait_ref| { + self.tcx.erase_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_ref), + ) + }) + .collect(), + ); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = self - .supertraits - .as_ref() - .unwrap() - .contains(&data.trait_ref(self.tcx).def_id); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { - ControlFlow::Continue(()) - } else { - t.super_visit_with(self) // POSSIBLY reporting an error - } + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = + self.supertraits.as_ref().unwrap().contains( + &data.trait_ref(self.tcx).fold_with( + &mut EraseEscapingBoundRegions { + tcx: self.tcx, + binder: ty::INNERMOST, + }, + ), + ); + + // only walk contained types if it's not a super trait + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) + } else { + t.super_visit_with(self) // POSSIBLY reporting an error } - AllowSelfProjections::No => t.super_visit_with(self), } + AllowSelfProjections::No => t.super_visit_with(self), } - _ => t.super_visit_with(self), } + _ => t.super_visit_with(self), } + } - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { - // Constants can only influence object safety if they are generic and reference `Self`. - // This is only possible for unevaluated constants, so we walk these here. - self.tcx.expand_abstract_consts(ct).super_visit_with(self) - } + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + // Constants can only influence object safety if they are generic and reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + self.tcx.expand_abstract_consts(ct).super_visit_with(self) } +} - value - .visit_with(&mut IllegalSelfTypeVisitor { - tcx, - trait_def_id, - supertraits: None, - allow_self_projections, - }) - .is_break() +struct EraseEscapingBoundRegions<'tcx> { + tcx: TyCtxt<'tcx>, + binder: ty::DebruijnIndex, +} + +impl<'tcx> TypeFolder> for EraseEscapingBoundRegions<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable>, + { + self.binder.shift_in(1); + let result = t.super_fold_with(self); + self.binder.shift_out(1); + result + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReBound(debruijn, _) = *r + && debruijn < self.binder + { + r + } else { + self.tcx.lifetimes.re_erased + } + } } pub fn contains_illegal_impl_trait_in_trait<'tcx>( diff --git a/tests/ui/object-safety/almost-supertrait-associated-type.rs b/tests/ui/object-safety/almost-supertrait-associated-type.rs new file mode 100644 index 0000000000000..963cdff526ee6 --- /dev/null +++ b/tests/ui/object-safety/almost-supertrait-associated-type.rs @@ -0,0 +1,60 @@ +// Test for fixed unsoundness in #126079. +// Enforces that the associated types that are object safe + +use std::marker::PhantomData; + +fn transmute(t: T) -> U { + (&PhantomData:: as &dyn Foo).transmute(t) + //~^ ERROR the trait `Foo` cannot be made into an object + //~| ERROR the trait `Foo` cannot be made into an object +} + +struct ActuallySuper; +struct NotActuallySuper; +trait Super { + type Assoc; +} + +trait Dyn { + type Out; +} +impl Dyn for dyn Foo + '_ { +//~^ ERROR the trait `Foo` cannot be made into an object + type Out = U; +} +impl + ?Sized, U> Super for S { + type Assoc = U; +} + +trait Foo: Super +where + ::Assoc: Super +{ + fn transmute(&self, t: T) -> >::Assoc; +} + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +impl Foo for PhantomData { + fn transmute(&self, t: T) -> T { + t + } +} +impl Super for PhantomData { + type Assoc = T; +} +impl Super for PhantomData { + type Assoc = T; +} + +fn main() { + let x = String::from("hello, world"); + let s = transmute::<&str, &'static str>(x.as_str()); + drop(x); + println!("> {s}"); +} diff --git a/tests/ui/object-safety/almost-supertrait-associated-type.stderr b/tests/ui/object-safety/almost-supertrait-associated-type.stderr new file mode 100644 index 0000000000000..97a51c2f38164 --- /dev/null +++ b/tests/ui/object-safety/almost-supertrait-associated-type.stderr @@ -0,0 +1,55 @@ +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:21:20 + | +LL | impl Dyn for dyn Foo + '_ { + | ^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:7:27 + | +LL | (&PhantomData:: as &dyn Foo).transmute(t) + | ^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:7:6 + | +LL | (&PhantomData:: as &dyn Foo).transmute(t) + | ^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + = note: required for the cast from `&PhantomData` to `&dyn Foo` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`. From f6fe7e49a2bc2ad14513aa609b67e188470309f6 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 14 Jul 2024 22:17:28 +0300 Subject: [PATCH 112/806] lib: replace some `mem::forget`'s with `ManuallyDrop` --- library/alloc/src/rc.rs | 47 +++++++++---------- library/alloc/src/sync.rs | 36 +++++++------- library/core/src/task/wake.rs | 15 ++---- library/proc_macro/src/bridge/buffer.rs | 11 ++--- library/proc_macro/src/bridge/client.rs | 4 +- library/std/src/os/fd/owned.rs | 6 +-- library/std/src/os/solid/io.rs | 6 +-- library/std/src/os/windows/io/handle.rs | 6 +-- library/std/src/os/windows/io/socket.rs | 7 +-- library/std/src/sys/pal/hermit/thread.rs | 6 +-- library/std/src/sys/pal/sgx/abi/tls/mod.rs | 4 +- .../src/sys/pal/sgx/abi/usercalls/alloc.rs | 7 ++- library/std/src/sys/pal/sgx/fd.rs | 10 ++-- library/std/src/sys/pal/teeos/thread.rs | 16 ++----- library/std/src/sys/pal/unix/thread.rs | 14 ++---- library/std/src/sys/pal/wasi/thread.rs | 10 ++-- library/std/src/thread/mod.rs | 9 ++-- .../libc_pthread_mutex_deadlock.stderr | 2 +- ..._pthread_rwlock_write_read_deadlock.stderr | 2 +- ...pthread_rwlock_write_write_deadlock.stderr | 2 +- 20 files changed, 88 insertions(+), 132 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 9982c8ea6dcbe..bfe3ea2080017 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -259,7 +259,7 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; +use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -908,19 +908,18 @@ impl Rc { #[stable(feature = "rc_unique", since = "1.4.0")] pub fn try_unwrap(this: Self) -> Result { if Rc::strong_count(&this) == 1 { - unsafe { - let val = ptr::read(&*this); // copy the contained object - let alloc = ptr::read(&this.alloc); // copy the allocator - - // Indicate to Weaks that they can't be promoted by decrementing - // the strong count, and then remove the implicit "strong weak" - // pointer while also handling drop logic by just crafting a - // fake Weak. - this.inner().dec_strong(); - let _weak = Weak { ptr: this.ptr, alloc }; - forget(this); - Ok(val) - } + let this = ManuallyDrop::new(this); + + let val: T = unsafe { ptr::read(&**this) }; // copy the contained object + let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator + + // Indicate to Weaks that they can't be promoted by decrementing + // the strong count, and then remove the implicit "strong weak" + // pointer while also handling drop logic by just crafting a + // fake Weak. + this.inner().dec_strong(); + let _weak = Weak { ptr: this.ptr, alloc }; + Ok(val) } else { Err(this) } @@ -1354,9 +1353,8 @@ impl Rc { #[stable(feature = "rc_raw", since = "1.17.0")] #[rustc_never_returns_null_ptr] pub fn into_raw(this: Self) -> *const T { - let ptr = Self::as_ptr(&this); - mem::forget(this); - ptr + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) } /// Consumes the `Rc`, returning the wrapped pointer and allocator. @@ -2127,7 +2125,7 @@ impl Rc<[T]> { } // All clear. Forget the guard so it doesn't free the new RcBox. - forget(guard); + mem::forget(guard); Self::from_ptr(ptr) } @@ -3080,9 +3078,7 @@ impl Weak { #[must_use = "losing the pointer will leak memory"] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_ptr(); - mem::forget(self); - result + mem::ManuallyDrop::new(self).as_ptr() } /// Consumes the `Weak`, returning the wrapped pointer and allocator. @@ -3762,10 +3758,11 @@ impl UniqueRcUninit { /// # Safety /// /// The data must have been initialized (by writing to [`Self::data_ptr()`]). - unsafe fn into_rc(mut self) -> Rc { - let ptr = self.ptr; - let alloc = self.alloc.take().unwrap(); - mem::forget(self); + unsafe fn into_rc(self) -> Rc { + let mut this = ManuallyDrop::new(self); + let ptr = this.ptr; + let alloc = this.alloc.take().unwrap(); + // SAFETY: The pointer is valid as per `UniqueRcUninit::new`, and the caller is responsible // for having initialized the data. unsafe { Rc::from_ptr_in(ptr.as_ptr(), alloc) } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a905a1e6b7e62..c36b8f6a1ac8a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -20,7 +20,7 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, align_of_val_raw}; +use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::Pin; @@ -960,16 +960,14 @@ impl Arc { acquire!(this.inner().strong); - unsafe { - let elem = ptr::read(&this.ptr.as_ref().data); - let alloc = ptr::read(&this.alloc); // copy the allocator + let this = ManuallyDrop::new(this); + let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; + let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator - // Make a weak pointer to clean up the implicit strong-weak reference - let _weak = Weak { ptr: this.ptr, alloc }; - mem::forget(this); + // Make a weak pointer to clean up the implicit strong-weak reference + let _weak = Weak { ptr: this.ptr, alloc }; - Ok(elem) - } + Ok(elem) } /// Returns the inner value, if the `Arc` has exactly one strong reference. @@ -1493,9 +1491,8 @@ impl Arc { #[stable(feature = "rc_raw", since = "1.17.0")] #[rustc_never_returns_null_ptr] pub fn into_raw(this: Self) -> *const T { - let ptr = Self::as_ptr(&this); - mem::forget(this); - ptr + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) } /// Consumes the `Arc`, returning the wrapped pointer and allocator. @@ -2801,9 +2798,7 @@ impl Weak { #[must_use = "losing the pointer will leak memory"] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_ptr(); - mem::forget(self); - result + ManuallyDrop::new(self).as_ptr() } /// Consumes the `Weak`, returning the wrapped pointer and allocator. @@ -3875,13 +3870,14 @@ impl UniqueArcUninit { /// # Safety /// /// The data must have been initialized (by writing to [`Self::data_ptr()`]). - unsafe fn into_arc(mut self) -> Arc { - let ptr = self.ptr; - let alloc = self.alloc.take().unwrap(); - mem::forget(self); + unsafe fn into_arc(self) -> Arc { + let mut this = ManuallyDrop::new(self); + let ptr = this.ptr.as_ptr(); + let alloc = this.alloc.take().unwrap(); + // SAFETY: The pointer is valid as per `UniqueArcUninit::new`, and the caller is responsible // for having initialized the data. - unsafe { Arc::from_ptr_in(ptr.as_ptr(), alloc) } + unsafe { Arc::from_ptr_in(ptr, alloc) } } } diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 86a965f68e085..e785d75a63d7a 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,10 +1,9 @@ #![stable(feature = "futures_api", since = "1.36.0")] -use crate::mem::transmute; - use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; +use crate::mem::{transmute, ManuallyDrop}; use crate::panic::AssertUnwindSafe; use crate::ptr; @@ -465,16 +464,14 @@ impl Waker { pub fn wake(self) { // The actual wakeup call is delegated through a virtual function call // to the implementation which is defined by the executor. - let wake = self.waker.vtable.wake; - let data = self.waker.data; // Don't call `drop` -- the waker will be consumed by `wake`. - crate::mem::forget(self); + let this = ManuallyDrop::new(self); // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `wake` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. - unsafe { (wake)(data) }; + unsafe { (this.waker.vtable.wake)(this.waker.data) }; } /// Wake up the task associated with this `Waker` without consuming the `Waker`. @@ -726,16 +723,14 @@ impl LocalWaker { pub fn wake(self) { // The actual wakeup call is delegated through a virtual function call // to the implementation which is defined by the executor. - let wake = self.waker.vtable.wake; - let data = self.waker.data; // Don't call `drop` -- the waker will be consumed by `wake`. - crate::mem::forget(self); + let this = ManuallyDrop::new(self); // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `wake` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. - unsafe { (wake)(data) }; + unsafe { (this.waker.vtable.wake)(this.waker.data) }; } /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 149767bf70521..78fcd1999b2f3 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -1,7 +1,7 @@ //! Buffer management for same-process client<->server communication. use std::io::{self, Write}; -use std::mem; +use std::mem::{self, ManuallyDrop}; use std::ops::{Deref, DerefMut}; use std::slice; @@ -129,17 +129,16 @@ impl Drop for Buffer { } impl From> for Buffer { - fn from(mut v: Vec) -> Self { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); - mem::forget(v); // This utility function is nested in here because it can *only* // be safely called on `Buffer`s created by *this* `proc_macro`. fn to_vec(b: Buffer) -> Vec { unsafe { - let Buffer { data, len, capacity, .. } = b; - mem::forget(b); - Vec::from_raw_parts(data, len, capacity) + let b = ManuallyDrop::new(b); + Vec::from_raw_parts(b.data, b.len, b.capacity) } } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index faca745e56f74..9658fc4840f67 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -51,9 +51,7 @@ macro_rules! define_client_handles { impl Encode for $oty { fn encode(self, w: &mut Writer, s: &mut S) { - let handle = self.handle; - mem::forget(self); - handle.encode(w, s); + mem::ManuallyDrop::new(self).handle.encode(w, s); } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index a1f83029d2727..800f3b0274faf 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -8,7 +8,7 @@ use crate::fmt; use crate::fs; use crate::io; use crate::marker::PhantomData; -use crate::mem::forget; +use crate::mem::ManuallyDrop; #[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))] use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -141,9 +141,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - let fd = self.fd; - forget(self); - fd + ManuallyDrop::new(self).fd } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 19b4fe22093c3..34fd745d4f9b4 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -49,7 +49,7 @@ use crate::fmt; use crate::marker::PhantomData; -use crate::mem::forget; +use crate::mem::ManuallyDrop; use crate::net; use crate::sys; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; @@ -149,9 +149,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - let fd = self.fd; - forget(self); - fd + ManuallyDrop::new(self).fd } } diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index a9d1983dce610..9865386e753df 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -7,7 +7,7 @@ use crate::fmt; use crate::fs; use crate::io; use crate::marker::PhantomData; -use crate::mem::{forget, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -319,9 +319,7 @@ impl AsRawHandle for OwnedHandle { impl IntoRawHandle for OwnedHandle { #[inline] fn into_raw_handle(self) -> RawHandle { - let handle = self.handle; - forget(self); - handle + ManuallyDrop::new(self).handle } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 6ffdf907c8ed3..a0a0cd2d2a3f2 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -6,8 +6,7 @@ use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use crate::fmt; use crate::io; use crate::marker::PhantomData; -use crate::mem; -use crate::mem::forget; +use crate::mem::{self, ManuallyDrop}; use crate::sys; #[cfg(not(target_vendor = "uwp"))] use crate::sys::cvt; @@ -191,9 +190,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - let socket = self.socket; - forget(self); - socket + ManuallyDrop::new(self).socket } } diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index a244b953d2a49..3723f03081c17 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -3,7 +3,7 @@ use super::hermit_abi; use crate::ffi::CStr; use crate::io; -use crate::mem; +use crate::mem::ManuallyDrop; use crate::num::NonZero; use crate::ptr; use crate::time::Duration; @@ -90,9 +90,7 @@ impl Thread { #[inline] pub fn into_id(self) -> Tid { - let id = self.tid; - mem::forget(self); - id + ManuallyDrop::new(self).tid } } diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 8a9ea4ac00df0..bab59a3422d15 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -95,8 +95,8 @@ impl Tls { #[allow(unused)] pub unsafe fn activate_persistent(self: Box) { // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. - unsafe { set_tls_ptr(core::ptr::addr_of!(*self) as _) }; - mem::forget(self); + let ptr = Box::into_raw(self).cast_const().cast::(); + unsafe { set_tls_ptr(ptr) }; } unsafe fn current<'a>() -> &'a Tls { diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index f99cea360f1f4..b625636752cc1 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -5,7 +5,7 @@ use crate::cell::UnsafeCell; use crate::cmp; use crate::convert::TryInto; use crate::intrinsics; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::ptr::{self, NonNull}; use crate::slice; @@ -176,6 +176,7 @@ unsafe impl UserSafe for [T] { /// are used solely to indicate intent: a mutable reference is for writing to /// user memory, an immutable reference for reading from user memory. #[unstable(feature = "sgx_platform", issue = "56975")] +#[repr(transparent)] pub struct UserRef(UnsafeCell); /// An owned type in userspace memory. `User` is equivalent to `Box` in /// enclave memory. Access to the memory is only allowed by copying to avoid @@ -266,9 +267,7 @@ where /// Converts this value into a raw pointer. The value will no longer be /// automatically freed. pub fn into_raw(self) -> *mut T { - let ret = self.0; - mem::forget(self); - ret.as_ptr() as _ + ManuallyDrop::new(self).0.as_ptr() as _ } } diff --git a/library/std/src/sys/pal/sgx/fd.rs b/library/std/src/sys/pal/sgx/fd.rs index b3686d0e28328..c41b527cff798 100644 --- a/library/std/src/sys/pal/sgx/fd.rs +++ b/library/std/src/sys/pal/sgx/fd.rs @@ -2,7 +2,7 @@ use fortanix_sgx_abi::Fd; use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; +use crate::mem::ManuallyDrop; use crate::sys::{AsInner, FromInner, IntoInner}; #[derive(Debug)] @@ -21,9 +21,7 @@ impl FileDesc { /// Extracts the actual file descriptor without closing it. pub fn into_raw(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd + ManuallyDrop::new(self).fd } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -70,9 +68,7 @@ impl AsInner for FileDesc { impl IntoInner for FileDesc { fn into_inner(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd + ManuallyDrop::new(self).fd } } diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index f4723b2ea46bf..ef40eba4df2be 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -1,9 +1,7 @@ -use core::convert::TryInto; - use crate::cmp; use crate::ffi::CStr; use crate::io; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; use crate::ptr; use crate::sys::os; @@ -113,11 +111,9 @@ impl Thread { /// must join, because no pthread_detach supported pub fn join(self) { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = self.into_id(); + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } pub fn id(&self) -> libc::pthread_t { @@ -125,9 +121,7 @@ impl Thread { } pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id + ManuallyDrop::new(self).id } } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 619f4e4121e73..483697b8597f8 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -1,7 +1,7 @@ use crate::cmp; use crate::ffi::CStr; use crate::io; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; use crate::ptr; use crate::sys::{os, stack_overflow}; @@ -268,11 +268,9 @@ impl Thread { } pub fn join(self) { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = self.into_id(); + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } pub fn id(&self) -> libc::pthread_t { @@ -280,9 +278,7 @@ impl Thread { } pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id + ManuallyDrop::new(self).id } } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 975eef2451f4c..2a3a39aafa704 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -172,12 +172,10 @@ impl Thread { pub fn join(self) { cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = mem::ManuallyDrop::new(self).id; + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); } } else { self.0 diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index c8ee365392f85..73bf562370829 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -165,7 +165,7 @@ use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; -use crate::mem::{self, forget}; +use crate::mem::{self, forget, ManuallyDrop}; use crate::num::NonZero; use crate::panic; use crate::panicking; @@ -514,11 +514,10 @@ impl Builder { MaybeDangling(mem::MaybeUninit::new(x)) } fn into_inner(self) -> T { - // SAFETY: we are always initialized. - let ret = unsafe { self.0.assume_init_read() }; // Make sure we don't drop. - mem::forget(self); - ret + let this = ManuallyDrop::new(self); + // SAFETY: we are always initialized. + unsafe { this.0.assume_init_read() } } } impl Drop for MaybeDangling { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index 987d0fc4c2d04..079c1729b600f 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -10,7 +10,7 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ error: deadlock: the evaluated program deadlocked --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC | -LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); +LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ the evaluated program deadlocked | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index bc9b15f293ef8..d03c6402d64ff 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -10,7 +10,7 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu error: deadlock: the evaluated program deadlocked --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC | -LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); +LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ the evaluated program deadlocked | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 66c142bbc5c80..73c5e77a1bce4 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -10,7 +10,7 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu error: deadlock: the evaluated program deadlocked --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC | -LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); +LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ the evaluated program deadlocked | = note: BACKTRACE: From 9d9b55cd2b14bce066cef83d9d85ba798a2ba95c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Jul 2024 21:25:03 +0200 Subject: [PATCH 113/806] make invalid_type_param_default lint show up in cargo future-compat reports and remove the feature gate that silenced the lint --- compiler/rustc_feature/src/removed.rs | 3 ++ compiler/rustc_feature/src/unstable.rs | 2 - .../src/collect/generics_of.rs | 2 - compiler/rustc_lint_defs/src/builtin.rs | 2 +- ...ate-default_type_parameter_fallback.stderr | 21 --------- tests/ui/impl-trait/where-allowed.stderr | 22 ++++++++++ tests/ui/issues/issue-26812.rs | 4 +- tests/ui/issues/issue-26812.stderr | 25 ++++++++++- .../lifetimes/unusual-rib-combinations.stderr | 11 +++++ ...ed-type-param-in-fn-with-assoc-type.stderr | 10 +++++ .../default_type_parameter_in_fn_or_impl.rs} | 0 ...efault_type_parameter_in_fn_or_impl.stderr | 43 +++++++++++++++++++ 12 files changed, 115 insertions(+), 30 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr rename tests/ui/{feature-gates/feature-gate-default_type_parameter_fallback.rs => type/default_type_parameter_in_fn_or_impl.rs} (100%) create mode 100644 tests/ui/type/default_type_parameter_in_fn_or_impl.stderr diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index f13aa506c1ec0..a78ae0d6993e2 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -75,6 +75,9 @@ declare_features! ( /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`. (removed, custom_derive, "1.32.0", Some(29644), Some("subsumed by `#[proc_macro_derive]`")), + /// Allows default type parameters to influence type inference. + (removed, default_type_parameter_fallback, "CURRENT_RUSTC_VERSION", Some(27336), + Some("never properly implemented; requires significant design work")), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.28.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`")), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 948499fb38fbf..407800ce60d5d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -428,8 +428,6 @@ declare_features! ( (unstable, custom_test_frameworks, "1.30.0", Some(50297)), /// Allows declarative macros 2.0 (`macro`). (unstable, decl_macro, "1.17.0", Some(39412)), - /// Allows default type parameters to influence type inference. - (unstable, default_type_parameter_fallback, "1.3.0", Some(27336)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 22d465c8e62be..398a496a3737f 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -323,8 +323,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if default.is_some() { match allow_defaults { Defaults::Allowed => {} - Defaults::FutureCompatDisallowed - if tcx.features().default_type_parameter_fallback => {} Defaults::FutureCompatDisallowed => { tcx.node_span_lint( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index aa7844f40121b..276a507d3e89b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1241,7 +1241,7 @@ declare_lint! { Deny, "type parameter default erroneously allowed in invalid location", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, reference: "issue #36887 ", }; } diff --git a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr b/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr deleted file mode 100644 index 308de2692930d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/feature-gate-default_type_parameter_fallback.rs:3:8 - | -LL | fn avg(_: T) {} - | ^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #36887 - = note: `#[deny(invalid_type_param_default)]` on by default - -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/feature-gate-default_type_parameter_fallback.rs:8:6 - | -LL | impl S {} - | ^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #36887 - -error: aborting due to 2 previous errors - diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index f0d259d01de94..1fb69db98c162 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -433,3 +433,25 @@ error: aborting due to 50 previous errors Some errors have detailed explanations: E0053, E0118, E0283, E0562, E0599, E0658, E0666. For more information about an error, try `rustc --explain E0053`. +Future incompatibility report: Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:239:7 + | +LL | impl T {} + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + +Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:246:36 + | +LL | fn in_method_generic_param_default(_: T) {} + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + diff --git a/tests/ui/issues/issue-26812.rs b/tests/ui/issues/issue-26812.rs index 3391ea4b350af..e0723e016b381 100644 --- a/tests/ui/issues/issue-26812.rs +++ b/tests/ui/issues/issue-26812.rs @@ -1,6 +1,6 @@ -#![feature(default_type_parameter_fallback)] - fn avg(_: T) {} //~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~| ERROR defaults for type parameters +//~| WARN previously accepted fn main() {} diff --git a/tests/ui/issues/issue-26812.stderr b/tests/ui/issues/issue-26812.stderr index c2a3d4b83d536..4a18b23fd8b13 100644 --- a/tests/ui/issues/issue-26812.stderr +++ b/tests/ui/issues/issue-26812.stderr @@ -1,9 +1,30 @@ error[E0128]: generic parameters with a default cannot use forward declared identifiers - --> $DIR/issue-26812.rs:3:10 + --> $DIR/issue-26812.rs:1:10 | LL | fn avg(_: T) {} | ^^^^^^^ defaulted generic parameters cannot be forward declared -error: aborting due to 1 previous error +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/issue-26812.rs:1:8 + | +LL | fn avg(_: T) {} + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0128`. +Future incompatibility report: Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/issue-26812.rs:1:8 + | +LL | fn avg(_: T) {} + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index 70f06b4be603c..3f97ae6c5bd54 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -68,3 +68,14 @@ error: aborting due to 8 previous errors Some errors have detailed explanations: E0106, E0214, E0308, E0770. For more information about an error, try `rustc --explain E0106`. +Future incompatibility report: Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/unusual-rib-combinations.rs:15:6 + | +LL | fn c() {} + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + diff --git a/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr b/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr index dc0bea58a70e8..bf8829c09257f 100644 --- a/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr +++ b/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr @@ -12,3 +12,13 @@ LL | foo::(); error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. +Future incompatibility report: Future breakage diagnostic: +warning: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/unbounded-type-param-in-fn-with-assoc-type.rs:3:11 + | +LL | fn foo() -> (T, U) { + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + diff --git a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.rs b/tests/ui/type/default_type_parameter_in_fn_or_impl.rs similarity index 100% rename from tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.rs rename to tests/ui/type/default_type_parameter_in_fn_or_impl.rs diff --git a/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr b/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr new file mode 100644 index 0000000000000..a3205cd3c29ca --- /dev/null +++ b/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr @@ -0,0 +1,43 @@ +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default_type_parameter_in_fn_or_impl.rs:3:8 + | +LL | fn avg(_: T) {} + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default_type_parameter_in_fn_or_impl.rs:8:6 + | +LL | impl S {} + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + +error: aborting due to 2 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default_type_parameter_in_fn_or_impl.rs:3:8 + | +LL | fn avg(_: T) {} + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + +Future breakage diagnostic: +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default_type_parameter_in_fn_or_impl.rs:8:6 + | +LL | impl S {} + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + From 489a77831ce61e3e3679f4fbcf93be42d3922e52 Mon Sep 17 00:00:00 2001 From: apoisternex Date: Sun, 14 Jul 2024 18:36:10 -0300 Subject: [PATCH 114/806] fix [`excessive_precision`] suggestions on float literal written in scientific notation fixes: #12954 changelog: fix [`excessive_precision`] suggestions on float literal written in scientific notation --- clippy_utils/src/numeric_literal.rs | 2 ++ tests/ui/excessive_precision.fixed | 4 ++++ tests/ui/excessive_precision.rs | 4 ++++ tests/ui/excessive_precision.stderr | 8 +++++++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index bbe4149fe2ab2..c5a34160e3dad 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -164,6 +164,8 @@ impl<'a> NumericLiteral<'a> { if !exponent.is_empty() && exponent != "0" { output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); + } else if exponent == "0" && self.fraction.is_none() && self.suffix.is_none() { + output.push_str(".0"); } } diff --git a/tests/ui/excessive_precision.fixed b/tests/ui/excessive_precision.fixed index cc5531035309b..372f64f7d99e2 100644 --- a/tests/ui/excessive_precision.fixed +++ b/tests/ui/excessive_precision.fixed @@ -78,4 +78,8 @@ fn main() { const NEG_INF1: f32 = -1.0e+33f32; const NEG_INF2: f64 = -1.0e+3300f64; const NEG_INF3: f32 = -3.40282357e+38_f32; + + // issue #12954 + const _: f64 = 3.0; + const _: f64 = 3.0000000000000000; } diff --git a/tests/ui/excessive_precision.rs b/tests/ui/excessive_precision.rs index fff986a8296ce..1e40efbf2452c 100644 --- a/tests/ui/excessive_precision.rs +++ b/tests/ui/excessive_precision.rs @@ -78,4 +78,8 @@ fn main() { const NEG_INF1: f32 = -1.0e+33f32; const NEG_INF2: f64 = -1.0e+3300f64; const NEG_INF3: f32 = -3.40282357e+38_f32; + + // issue #12954 + const _: f64 = 3.0000000000000000e+00; + const _: f64 = 3.0000000000000000; } diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index 22dd96e53bdbe..6d8e166a649d8 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -91,5 +91,11 @@ error: float has excessive precision LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64` -error: aborting due to 15 previous errors +error: float has excessive precision + --> tests/ui/excessive_precision.rs:83:20 + | +LL | const _: f64 = 3.0000000000000000e+00; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `3.0` + +error: aborting due to 16 previous errors From c1c945a350ab7c2d82013f46fc94d332d2964516 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jul 2024 21:03:08 -0400 Subject: [PATCH 115/806] Move rustc_infer::infer::error_reporting to rustc_infer::error_reporting::infer --- tests/ui/track-diagnostics.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/track-diagnostics.stderr b/tests/ui/track-diagnostics.stderr index 410e80f25280a..3c7577dd003cc 100644 --- a/tests/ui/track-diagnostics.stderr +++ b/tests/ui/track-diagnostics.stderr @@ -3,7 +3,7 @@ error[E0308]: mismatched types | LL | const S: A = B; | ^ expected `A`, found `B` --Ztrack-diagnostics: created at compiler/rustc_infer/src/infer/error_reporting/mod.rs:LL:CC +-Ztrack-diagnostics: created at compiler/rustc_infer/src/error_reporting/infer/mod.rs:LL:CC error: aborting due to 1 previous error From e1e5b8a2b69da18b11147aa8d18e731ee32d9819 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 16 Jul 2024 05:14:32 +0000 Subject: [PATCH 116/806] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e90d3732ca577..2c29af8b9357f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -99b7134389e9766462601a2fc4013840b9d31745 +5c8488605624d67b272953bc21d41db60dbd5654 From 7f8a54bbee4fec4a6885c7554a2864a62bf40638 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 Jul 2024 09:59:39 +0200 Subject: [PATCH 117/806] Switch token trees to use Symbols --- src/tools/rust-analyzer/Cargo.lock | 8 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 1 + .../rust-analyzer/crates/cfg/src/cfg_expr.rs | 13 +- src/tools/rust-analyzer/crates/cfg/src/lib.rs | 2 +- .../rust-analyzer/crates/hir-def/src/attr.rs | 34 ++--- .../crates/hir-def/src/builtin_type.rs | 27 +++- .../rust-analyzer/crates/hir-def/src/data.rs | 2 +- .../crates/hir-def/src/data/adt.rs | 22 ++-- .../rust-analyzer/crates/hir-def/src/db.rs | 2 +- .../crates/hir-def/src/nameres.rs | 15 +-- .../hir-def/src/nameres/attr_resolution.rs | 12 +- .../crates/hir-def/src/nameres/collector.rs | 10 +- .../crates/hir-def/src/nameres/proc_macro.rs | 3 +- .../crates/hir-expand/src/attrs.rs | 33 ++--- .../hir-expand/src/builtin_derive_macro.rs | 22 ++-- .../crates/hir-expand/src/builtin_fn_macro.rs | 74 +++++------ .../crates/hir-expand/src/cfg_process.rs | 9 +- .../crates/hir-expand/src/declarative.rs | 8 +- .../crates/hir-expand/src/fixup.rs | 31 ++--- .../crates/hir-expand/src/inert_attr_macro.rs | 12 +- .../crates/hir-expand/src/mod_path.rs | 24 ++-- .../crates/hir-expand/src/name.rs | 20 ++- .../crates/hir-expand/src/quote.rs | 46 +++---- .../diagnostics/match_check/pat_analysis.rs | 5 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 32 +++-- .../rust-analyzer/crates/ide-db/src/defs.rs | 10 +- .../rust-analyzer/crates/intern/src/symbol.rs | 26 ++++ .../crates/intern/src/symbol/symbols.rs | 24 ++++ src/tools/rust-analyzer/crates/mbe/Cargo.toml | 1 + .../rust-analyzer/crates/mbe/src/benchmark.rs | 7 +- .../rust-analyzer/crates/mbe/src/expander.rs | 6 +- .../crates/mbe/src/expander/matcher.rs | 48 +++---- .../crates/mbe/src/expander/transcriber.rs | 28 ++--- .../rust-analyzer/crates/mbe/src/parser.rs | 42 +++---- .../crates/mbe/src/syntax_bridge.rs | 28 +++-- .../crates/mbe/src/to_parser_input.rs | 11 +- .../crates/proc-macro-api/Cargo.toml | 3 +- .../crates/proc-macro-api/src/lib.rs | 3 +- .../crates/proc-macro-api/src/msg.rs | 11 +- .../crates/proc-macro-api/src/msg/flat.rs | 21 ++-- .../crates/proc-macro-srv/Cargo.toml | 1 + .../crates/proc-macro-srv/src/lib.rs | 3 +- .../crates/proc-macro-srv/src/server_impl.rs | 4 +- .../src/server_impl/rust_analyzer_span.rs | 118 +++++++++++++++--- .../proc-macro-srv/src/server_impl/symbol.rs | 1 - .../src/server_impl/token_id.rs | 38 +++--- .../crates/test-fixture/Cargo.toml | 3 +- .../crates/test-fixture/src/lib.rs | 5 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/tt/src/iter.rs | 5 +- src/tools/rust-analyzer/crates/tt/src/lib.rs | 76 +++++------ 51 files changed, 593 insertions(+), 399 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 500a150b57b84..241392edb1e5d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -149,6 +149,7 @@ dependencies = [ "mbe", "oorandom", "rustc-hash", + "smol_str", "syntax", "tt", ] @@ -1045,6 +1046,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "cov-mark", + "intern", "parser", "rustc-hash", "smallvec", @@ -1324,8 +1326,8 @@ version = "0.0.0" dependencies = [ "base-db", "indexmap", + "intern", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mbe", "paths", "rustc-hash", "serde", @@ -1343,6 +1345,7 @@ version = "0.0.0" dependencies = [ "base-db", "expect-test", + "intern", "libloading", "mbe", "memmap2", @@ -1966,6 +1969,7 @@ dependencies = [ "base-db", "cfg", "hir-expand", + "intern", "rustc-hash", "span", "stdx", @@ -2218,8 +2222,8 @@ name = "tt" version = "0.0.0" dependencies = [ "arrayvec", + "intern", "ra-ap-rustc_lexer", - "smol_str", "stdx", "text-size", ] diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 9b3a5026ac800..784e86649d1ab 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -16,6 +16,7 @@ rustc-hash.workspace = true # locals deps tt.workspace = true +smol_str.workspace = true [dev-dependencies] expect-test = "1.4.1" diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index b7dbb7b5fdd79..9c95f0e4e4baa 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -4,7 +4,7 @@ use std::{fmt, slice::Iter as SliceIter}; -use tt::SmolStr; +use smol_str::SmolStr; /// A simple configuration value passed in from the outside. #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] @@ -66,7 +66,7 @@ impl CfgExpr { fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(), + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(CfgExpr::Invalid), }; @@ -77,10 +77,9 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); - // FIXME: escape? raw string? - let value = - SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); - CfgAtom::KeyValue { key: name, value }.into() + // FIXME: escape? + let value = literal.symbol.as_str().into(); + CfgAtom::KeyValue { key: name.as_str().into(), value }.into() } _ => return Some(CfgExpr::Invalid), } @@ -96,7 +95,7 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option _ => CfgExpr::Invalid, } } - _ => CfgAtom::Flag(name).into(), + _ => CfgAtom::Flag(name.as_str().into()).into(), }; // Eat comma separator diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 8b30286a0a8a8..5ef7a104dda59 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -8,10 +8,10 @@ mod tests; use std::fmt; use rustc_hash::FxHashSet; -use tt::SmolStr; pub use cfg_expr::{CfgAtom, CfgExpr}; pub use dnf::DnfExpr; +use smol_str::SmolStr; /// Configuration options used for conditional compilation on items with `cfg` attributes. /// We have two kind of options in different namespaces: atomic options like `unix`, and diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 5bd60c58e9b7e..1a5ac96aa29bb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -159,14 +159,14 @@ impl Attrs { pub fn has_doc_hidden(&self) -> bool { self.by_key("doc").tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden") + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) }) } pub fn has_doc_notable_trait(&self) -> bool { self.by_key("doc").tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait") + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) }) } @@ -267,7 +267,7 @@ impl DocExpr { fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(), + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(DocExpr::Invalid), }; @@ -275,13 +275,16 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option let ret = match it.as_slice().first() { Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { match it.as_slice().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + kind: tt::LitKind::Str, + .. + }))) => { it.next(); it.next(); // FIXME: escape? raw string? - let value = - SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); - DocAtom::KeyValue { key: name, value }.into() + let value = SmolStr::new(text.as_str()); + DocAtom::KeyValue { key: name.as_str().into(), value }.into() } _ => return Some(DocExpr::Invalid), } @@ -294,7 +297,7 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option _ => DocExpr::Invalid, } } - _ => DocAtom::Flag(name).into(), + _ => DocAtom::Flag(name.as_str().into()).into(), }; // Eat comma separator @@ -311,10 +314,11 @@ fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { .token_trees .iter() .filter_map(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - // FIXME: escape? raw string? - Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"'))) - } + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, + symbol: text, + .. + })) => Some(SmolStr::new(text.as_str())), _ => None, }) .collect() @@ -598,14 +602,14 @@ impl<'attr> AttrQuery<'attr> { /// #[doc(html_root_url = "url")] /// ^^^^^^^^^^^^^ key /// ``` - pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&SmolStr> { + pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&str> { self.tt_values().find_map(|tt| { let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, ..} )) if text == key)) + .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if sym.as_str() == key)) .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), _ => None } }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs index 6dc1c4546e804..14b9af84e6ffb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs @@ -6,7 +6,7 @@ use std::fmt; use hir_expand::name::{AsName, Name}; -use intern::sym; +use intern::{sym, Symbol}; /// Different signed int types. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BuiltinInt { @@ -143,6 +143,18 @@ impl BuiltinInt { }; Some(res) } + pub fn from_suffix_sym(suffix: &Symbol) -> Option { + let res = match suffix { + s if *s == sym::isize => Self::Isize, + s if *s == sym::i8 => Self::I8, + s if *s == sym::i16 => Self::I16, + s if *s == sym::i32 => Self::I32, + s if *s == sym::i64 => Self::I64, + s if *s == sym::i128 => Self::I128, + _ => return None, + }; + Some(res) + } } #[rustfmt::skip] @@ -160,6 +172,19 @@ impl BuiltinUint { }; Some(res) } + pub fn from_suffix_sym(suffix: &Symbol) -> Option { + let res = match suffix { + s if *s == sym::usize => Self::Usize, + s if *s == sym::u8 => Self::U8, + s if *s == sym::u16 => Self::U16, + s if *s == sym::u32 => Self::U32, + s if *s == sym::u64 => Self::U64, + s if *s == sym::u128 => Self::U128, + + _ => return None, + }; + Some(res) + } } #[rustfmt::skip] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 964a5f04bfd22..e2bb02c0c13ff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -150,7 +150,7 @@ fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> { let mut indices = Vec::new(); for args in tt.token_trees.chunks(2) { match &args[0] { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.text.parse() { + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { Ok(index) => indices.push(index), Err(_) => break, }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 0fe73418e51fc..3942c2a98af78 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -9,7 +9,7 @@ use hir_expand::{ name::{AsName, Name}, HirFileId, InFile, }; -use intern::Interned; +use intern::{sym, Interned}; use la_arena::Arena; use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; @@ -112,12 +112,12 @@ fn parse_repr_tt(tt: &Subtree) -> Option { let mut tts = tt.token_trees.iter().peekable(); while let Some(tt) = tts.next() { if let TokenTree::Leaf(Leaf::Ident(ident)) = tt { - flags.insert(match &*ident.text { - "packed" => { + flags.insert(match &ident.sym { + s if *s == sym::packed => { let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() { tts.next(); if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { - lit.text.parse().unwrap_or_default() + lit.symbol.as_str().parse().unwrap_or_default() } else { 0 } @@ -129,11 +129,11 @@ fn parse_repr_tt(tt: &Subtree) -> Option { Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack }); ReprFlags::empty() } - "align" => { + s if *s == sym::align => { if let Some(TokenTree::Subtree(tt)) = tts.peek() { tts.next(); if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { - if let Ok(align) = lit.text.parse() { + if let Ok(align) = lit.symbol.as_str().parse() { let align = Align::from_bytes(align).ok(); max_align = max_align.max(align); } @@ -141,13 +141,13 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } ReprFlags::empty() } - "C" => ReprFlags::IS_C, - "transparent" => ReprFlags::IS_TRANSPARENT, - "simd" => ReprFlags::IS_SIMD, + s if *s == sym::C => ReprFlags::IS_C, + s if *s == sym::transparent => ReprFlags::IS_TRANSPARENT, + s if *s == sym::simd => ReprFlags::IS_SIMD, repr => { - if let Some(builtin) = BuiltinInt::from_suffix(repr) + if let Some(builtin) = BuiltinInt::from_suffix_sym(repr) .map(Either::Left) - .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right)) + .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right)) { int = Some(match builtin { Either::Left(bi) => match bi { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index eac8f0dd74ac8..1f728a0b38b52 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -278,7 +278,7 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { tt.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); for output in segments.skip(1) { match output { - [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "no_std" => { + [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => { return true } _ => {} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index b0543727c27d0..6861e0f2f5be5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -63,6 +63,7 @@ use base_db::{CrateId, FileId}; use hir_expand::{ name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; +use intern::Symbol; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; @@ -148,11 +149,11 @@ struct DefMapCrateData { proc_macro_loading_error: Option>, /// Custom attributes registered with `#![register_attr]`. - registered_attrs: Vec, + registered_attrs: Vec, /// Custom tool modules registered with `#![register_tool]`. - registered_tools: Vec, + registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. - unstable_features: FxHashSet, + unstable_features: FxHashSet, /// #[rustc_coherence_is_core] rustc_coherence_is_core: bool, no_core: bool, @@ -170,7 +171,7 @@ impl DefMapCrateData { fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, registered_attrs: Vec::new(), - registered_tools: PREDEFINED_TOOLS.into(), + registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, no_core: false, @@ -447,15 +448,15 @@ impl DefMap { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } - pub fn registered_tools(&self) -> &[SmolStr] { + pub fn registered_tools(&self) -> &[Symbol] { &self.data.registered_tools } - pub fn registered_attrs(&self) -> &[SmolStr] { + pub fn registered_attrs(&self) -> &[Symbol] { &self.data.registered_attrs } - pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool { + pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { self.data.unstable_features.contains(feature) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index f842027d642a6..747860fd8e17b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -7,7 +7,7 @@ use hir_expand::{ MacroCallId, MacroCallKind, MacroDefId, }; use span::SyntaxContextId; -use syntax::{ast, SmolStr}; +use syntax::ast; use triomphe::Arc; use crate::{ @@ -79,20 +79,20 @@ impl DefMap { let segments = path.segments(); if let Some(name) = segments.first() { - let name = name.to_smol_str(); - let pred = |n: &_| *n == name; + let name = name.symbol(); + let pred = |n: &_| *n == *name; - let is_tool = self.data.registered_tools.iter().map(SmolStr::as_str).any(pred); + let is_tool = self.data.registered_tools.iter().any(pred); // FIXME: tool modules can be shadowed by actual modules if is_tool { return true; } if segments.len() == 1 { - if find_builtin_attr_idx(&name).is_some() { + if find_builtin_attr_idx(name).is_some() { return true; } - if self.data.registered_attrs.iter().map(SmolStr::as_str).any(pred) { + if self.data.registered_attrs.iter().any(pred) { return true; } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 13abcce78ead4..a614d99de6c1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -317,20 +317,20 @@ impl DefCollector<'_> { .into_iter() .flatten() .filter_map(|(feat, _)| match feat.segments() { - [name] => Some(name.to_smol_str()), + [name] => Some(name.symbol().clone()), _ => None, }); crate_data.unstable_features.extend(features); } () if *attr_name == sym::register_attr.clone() => { if let Some(ident) = attr.single_ident_value() { - crate_data.registered_attrs.push(ident.text.clone()); + crate_data.registered_attrs.push(ident.sym.clone()); cov_mark::hit!(register_attr); } } () if *attr_name == sym::register_tool.clone() => { if let Some(ident) = attr.single_ident_value() { - crate_data.registered_tools.push(ident.text.clone()); + crate_data.registered_tools.push(ident.sym.clone()); cov_mark::hit!(register_tool); } } @@ -2129,9 +2129,7 @@ impl ModCollector<'_, '_> { let is_export = export_attr.exists(); let local_inner = if is_export { export_attr.tt_values().flat_map(|it| it.token_trees.iter()).any(|it| match it { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - ident.text.contains("local_inner_macros") - } + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros, _ => false, }) } else { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index 5052708dc9391..4789b1f014579 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -1,6 +1,7 @@ //! Nameres-specific procedural macro data and helpers. use hir_expand::name::{AsName, Name}; +use intern::sym; use crate::attr::Attrs; use crate::tt::{Leaf, TokenTree}; @@ -67,7 +68,7 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Nam TokenTree::Leaf(Leaf::Punct(comma)), TokenTree::Leaf(Leaf::Ident(attributes)), TokenTree::Subtree(helpers) - ] if comma.char == ',' && attributes.text == "attributes" => + ] if comma.char == ',' && attributes.sym == sym::attributes => { let helpers = helpers .token_trees diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 49a104fa118e6..8ef7b7049ae7d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -62,7 +62,7 @@ impl RawAttrs { Attr { id, input: Some(Box::new(AttrInput::Literal(tt::Literal { - text, + symbol: text, span, kind, suffix: None, @@ -243,7 +243,7 @@ impl Attr { let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { let token = lit.token(); - Some(Box::new(AttrInput::Literal(token_to_literal(token.text().into(), span)))) + Some(Box::new(AttrInput::Literal(token_to_literal(token.text(), span)))) } else if let Some(tt) = ast.token_tree() { let tree = syntax_node_to_token_tree( tt.syntax(), @@ -260,8 +260,8 @@ impl Attr { fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option { if matches!(tt, - [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, .. })), ..] - if text == "unsafe" + [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..] + if *sym == sym::unsafe_ ) { match tt.get(1) { Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees, @@ -313,10 +313,10 @@ impl Attr { pub fn string_value(&self) -> Option<&str> { match self.input.as_deref()? { AttrInput::Literal(tt::Literal { - text, + symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), .. - }) => Some(text), + }) => Some(text.as_str()), _ => None, } } @@ -324,23 +324,24 @@ impl Attr { /// #[path = "string"] pub fn string_value_with_span(&self) -> Option<(&str, span::Span)> { match self.input.as_deref()? { - AttrInput::Literal(it) => match it.text.strip_prefix('r') { - Some(it) => it.trim_matches('#'), - None => it.text.as_str(), - } - .strip_prefix('"')? - .strip_suffix('"') - .zip(Some(it.span)), + AttrInput::Literal(tt::Literal { + symbol: text, + kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), + span, + suffix: _, + }) => Some((text.as_str(), *span)), _ => None, } } pub fn string_value_unescape(&self) -> Option> { match self.input.as_deref()? { - AttrInput::Literal(tt::Literal { text, kind: tt::LitKind::StrRaw(_), .. }) => { - Some(Cow::Borrowed(text)) + AttrInput::Literal(tt::Literal { + symbol: text, kind: tt::LitKind::StrRaw(_), .. + }) => Some(Cow::Borrowed(text.as_str())), + AttrInput::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. }) => { + unescape(text.as_str()) } - AttrInput::Literal(tt::Literal { text, kind: tt::LitKind::Str, .. }) => unescape(text), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 180d8f05627f4..b2924ae6736c7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -82,7 +82,7 @@ enum VariantShape { } fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator { - (0..n).map(move |it| tt::Ident::new(format!("f{it}"), span)) + (0..n).map(move |it| tt::Ident::new(&format!("f{it}"), span)) } impl VariantShape { @@ -693,14 +693,14 @@ fn partial_eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult } [first, rest @ ..] => { let rest = rest.iter().map(|it| { - let t1 = tt::Ident::new(format!("{}_self", it.text), it.span); - let t2 = tt::Ident::new(format!("{}_other", it.text), it.span); + let t1 = tt::Ident::new(&format!("{}_self", it.sym), it.span); + let t2 = tt::Ident::new(&format!("{}_other", it.sym), it.span); let and_and = and_and(span); quote!(span =>#and_and #t1 .eq( #t2 )) }); let first = { - let t1 = tt::Ident::new(format!("{}_self", first.text), first.span); - let t2 = tt::Ident::new(format!("{}_other", first.text), first.span); + let t1 = tt::Ident::new(&format!("{}_self", first.sym), first.span); + let t2 = tt::Ident::new(&format!("{}_other", first.sym), first.span); quote!(span =>#t1 .eq( #t2 )) }; quote!(span =>#first ##rest) @@ -730,7 +730,7 @@ fn self_and_other_patterns( let self_patterns = adt.shape.as_pattern_map( name, |it| { - let t = tt::Ident::new(format!("{}_self", it.text), it.span); + let t = tt::Ident::new(&format!("{}_self", it.sym), it.span); quote!(span =>#t) }, span, @@ -738,7 +738,7 @@ fn self_and_other_patterns( let other_patterns = adt.shape.as_pattern_map( name, |it| { - let t = tt::Ident::new(format!("{}_other", it.text), it.span); + let t = tt::Ident::new(&format!("{}_other", it.sym), it.span); quote!(span =>#t) }, span, @@ -776,8 +776,8 @@ fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { |(pat1, pat2, fields)| { let mut body = quote!(span =>#krate::cmp::Ordering::Equal); for f in fields.into_iter().rev() { - let t1 = tt::Ident::new(format!("{}_self", f.text), f.span); - let t2 = tt::Ident::new(format!("{}_other", f.text), f.span); + let t1 = tt::Ident::new(&format!("{}_self", f.sym), f.span); + let t2 = tt::Ident::new(&format!("{}_other", f.sym), f.span); body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span); } let fat_arrow = fat_arrow(span); @@ -838,8 +838,8 @@ fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult let mut body = quote!(span =>#krate::option::Option::Some(#krate::cmp::Ordering::Equal)); for f in fields.into_iter().rev() { - let t1 = tt::Ident::new(format!("{}_self", f.text), f.span); - let t2 = tt::Ident::new(format!("{}_other", f.text), f.span); + let t1 = tt::Ident::new(&format!("{}_self", f.sym), f.span); + let t2 = tt::Ident::new(&format!("{}_other", f.sym), f.span); body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span); } let fat_arrow = fat_arrow(span); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index fb12adb87cb34..4b1a3d697280c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,10 +1,9 @@ //! Builtin macro -use ::tt::SmolStr; use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; -use intern::sym; +use intern::{sym, Symbol}; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; @@ -181,10 +180,10 @@ fn line_expand( ExpandResult::ok(tt::Subtree { delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text: "0".into(), + symbol: sym::INTEGER_0.clone(), span, kind: tt::LitKind::Integer, - suffix: Some(Box::new("u32".into())), + suffix: Some(sym::u32.clone()), }))]), }) } @@ -301,12 +300,12 @@ fn format_args_nl_expand( let mut tt = tt.clone(); tt.delimiter.kind = tt::DelimiterKind::Parenthesis; if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text, + symbol: text, kind: tt::LitKind::Str, .. }))) = tt.token_trees.first_mut() { - *text = format_smolstr!("{text}\\n"); + *text = Symbol::intern(&format_smolstr!("{}\\n", text.as_str())); } ExpandResult::ok(quote! {span => builtin #pound format_args #tt @@ -460,14 +459,14 @@ fn compile_error_expand( ) -> ExpandResult { let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text, + symbol: text, span: _, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), suffix: _, }))] => // FIXME: Use the span here! { - ExpandError::other(Box::from(&*unescape_str(text))) + ExpandError::other(Box::from(unescape_str(text).as_str())) } _ => ExpandError::other("`compile_error!` argument must be a string"), }; @@ -507,18 +506,20 @@ fn concat_expand( // as-is. match it.kind { tt::LitKind::Char => { - if let Ok(c) = unescape_char(&it.text) { + if let Ok(c) = unescape_char(it.symbol.as_str()) { text.extend(c.escape_default()); } record_span(it.span); } - tt::LitKind::Integer | tt::LitKind::Float => format_to!(text, "{}", it.text), + tt::LitKind::Integer | tt::LitKind::Float => { + format_to!(text, "{}", it.symbol.as_str()) + } tt::LitKind::Str => { - text.push_str(&it.text); + text.push_str(it.symbol.as_str()); record_span(it.span); } tt::LitKind::StrRaw(_) => { - format_to!(text, "{}", it.text.escape_debug()); + format_to!(text, "{}", it.symbol.as_str().escape_debug()); record_span(it.span); } tt::LitKind::Byte @@ -531,9 +532,9 @@ fn concat_expand( } // handle boolean literals tt::TokenTree::Leaf(tt::Leaf::Ident(id)) - if i % 2 == 0 && (id.text == "true" || id.text == "false") => + if i % 2 == 0 && (id.sym == sym::true_ || id.sym == sym::false_) => { - text.push_str(id.text.as_str()); + text.push_str(id.sym.as_str()); record_span(id.span); } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), @@ -562,21 +563,21 @@ fn concat_bytes_expand( }; for (i, t) in tt.token_trees.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, span, kind, suffix: _ })) => { + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind, suffix: _ })) => { record_span(*span); match kind { tt::LitKind::Byte => { - if let Ok(b) = unescape_byte(text) { + if let Ok(b) = unescape_byte(text.as_str()) { bytes.extend( b.escape_ascii().filter_map(|it| char::from_u32(it as u32)), ); } } tt::LitKind::ByteStr => { - bytes.push_str(text); + bytes.push_str(text.as_str()); } tt::LitKind::ByteStrRaw(_) => { - bytes.extend(text.escape_debug()); + bytes.extend(text.as_str().escape_debug()); } _ => { err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); @@ -602,7 +603,7 @@ fn concat_bytes_expand( value: tt::Subtree { delimiter: tt::Delimiter::invisible_spanned(span), token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text: bytes.into(), + symbol: Symbol::intern(&bytes), span, kind: tt::LitKind::ByteStr, suffix: None, @@ -621,24 +622,24 @@ fn concat_bytes_expand_subtree( for (ti, tt) in tree.token_trees.iter().enumerate() { match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text, + symbol: text, span, kind: tt::LitKind::Byte, suffix: _, })) => { - if let Ok(b) = unescape_byte(text) { + if let Ok(b) = unescape_byte(text.as_str()) { bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); } record_span(*span); } tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text, + symbol: text, span, kind: tt::LitKind::Integer, suffix: _, })) => { record_span(*span); - if let Ok(b) = text.parse::() { + if let Ok(b) = text.as_str().parse::() { bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); } } @@ -662,7 +663,7 @@ fn concat_idents_expand( for (i, t) in tt.token_trees.iter().enumerate() { match t { tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => { - ident.push_str(id.text.as_str()); + ident.push_str(id.sym.as_str()); } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { @@ -671,7 +672,7 @@ fn concat_idents_expand( } } // FIXME merge spans - let ident = tt::Ident { text: ident.into(), span, is_raw: tt::IdentIsRaw::No }; + let ident = tt::Ident { sym: Symbol::intern(&ident), span, is_raw: tt::IdentIsRaw::No }; ExpandResult { value: quote!(span =>#ident), err } } @@ -694,12 +695,12 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result<(SmolStr, Span), ExpandError> { +fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { tt.token_trees .first() .and_then(|tt| match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text, + symbol: text, span, kind: tt::LitKind::Str, suffix: _, @@ -739,7 +740,7 @@ pub fn include_input_to_file_id( arg_id: MacroCallId, arg: &tt::Subtree, ) -> Result { - relative_file(db, arg_id, &parse_string(arg)?.0, false) + relative_file(db, arg_id, parse_string(arg)?.0.as_str(), false) } fn include_bytes_expand( @@ -752,7 +753,7 @@ fn include_bytes_expand( let res = tt::Subtree { delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - text: r#"b"""#.into(), + symbol: Symbol::empty(), span, kind: tt::LitKind::ByteStrRaw(1), suffix: None, @@ -778,7 +779,7 @@ fn include_str_expand( // it's unusual to `include_str!` a Rust file), but we can return an empty string. // Ideally, we'd be able to offer a precise expansion if the user asks for macro // expansion. - let file_id = match relative_file(db, arg_id, &path, true) { + let file_id = match relative_file(db, arg_id, path.as_str(), true) { Ok(file_id) => file_id, Err(_) => { return ExpandResult::ok(quote!(span =>"")); @@ -791,9 +792,9 @@ fn include_str_expand( ExpandResult::ok(quote!(span =>#text)) } -fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option { +fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; - db.crate_graph()[krate].env.get(key).map(|it| it.escape_debug().to_string()) + db.crate_graph()[krate].env.get(key.as_str()).map(|it| it.escape_debug().to_string()) } fn env_expand( @@ -813,7 +814,7 @@ fn env_expand( let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| { // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. - if key == "OUT_DIR" { + if key.as_str() == "OUT_DIR" { err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#)); } @@ -867,15 +868,16 @@ fn quote_expand( ) } -fn unescape_str(s: &SmolStr) -> SmolStr { - if s.contains('\\') { +fn unescape_str(s: &Symbol) -> Symbol { + if s.as_str().contains('\\') { + let s = s.as_str(); let mut buf = String::with_capacity(s.len()); unescape_unicode(s, Mode::Str, &mut |_, c| { if let Ok(c) = c { buf.push(c) } }); - buf.into() + Symbol::intern(&buf) } else { s.clone() } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 55ae19068f9c6..5f038cfe6871d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -9,7 +9,6 @@ use syntax::{ AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T, }; use tracing::{debug, warn}; -use tt::SmolStr; use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; @@ -263,7 +262,7 @@ where let name = match iter.next() { None => return None, Some(NodeOrToken::Token(element)) => match element.kind() { - syntax::T![ident] => SmolStr::new(element.text()), + syntax::T![ident] => element.text().to_owned(), _ => return Some(CfgExpr::Invalid), }, Some(_) => return Some(CfgExpr::Invalid), @@ -302,13 +301,13 @@ where if (value_token.kind() == syntax::SyntaxKind::STRING) => { let value = value_token.text(); - let value = SmolStr::new(value.trim_matches('"')); - Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value })) + let value = value.trim_matches('"').into(); + Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name.into(), value })) } _ => None, } } - _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), + _ => Some(CfgExpr::Atom(CfgAtom::Flag(name.into()))), }, }; if let Some(NodeOrToken::Token(element)) = iter.peek() { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 2e7865ed3bf8c..3d3df83361814 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -120,10 +120,10 @@ impl DeclarativeMacroExpander { .token_tree_value()? .token_trees { - [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text { - "transparent" => Some(Transparency::Transparent), - "semitransparent" => Some(Transparency::SemiTransparent), - "opaque" => Some(Transparency::Opaque), + [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym { + s if *s == sym::transparent => Some(Transparency::Transparent), + s if *s == sym::semitransparent => Some(Transparency::SemiTransparent), + s if *s == sym::opaque => Some(Transparency::Opaque), _ => None, }, _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 2896afed084fd..cf51535630f5e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -1,6 +1,7 @@ //! To make attribute macros work reliably when typing, we need to take care to //! fix up syntax errors in the code we're passing to them. +use intern::sym; use mbe::DocCommentDesugarMode; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; @@ -80,7 +81,7 @@ pub(crate) fn fixup_syntax( original.push(original_tree); let span = span_map.span_for_range(node_range); let replacement = Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: Span { range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END), anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, @@ -100,7 +101,7 @@ pub(crate) fn fixup_syntax( // incomplete field access: some_expr.| append.insert(node.clone().into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }), @@ -138,7 +139,7 @@ pub(crate) fn fixup_syntax( }; append.insert(if_token.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }), @@ -169,7 +170,7 @@ pub(crate) fn fixup_syntax( }; append.insert(while_token.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }), @@ -217,7 +218,7 @@ pub(crate) fn fixup_syntax( }; append.insert(match_token.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }), @@ -247,12 +248,12 @@ pub(crate) fn fixup_syntax( }; let [pat, in_token, iter] = [ - "_", - "in", - "__ra_fixup" - ].map(|text| + sym::underscore.clone(), + sym::in_.clone(), + sym::__ra_fixup.clone(), + ].map(|sym| Leaf::Ident(Ident { - text: text.into(), + sym, span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }), @@ -286,7 +287,7 @@ pub(crate) fn fixup_syntax( if it.name_ref().is_some() && it.expr().is_none() { append.insert(colon.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }) @@ -299,7 +300,7 @@ pub(crate) fn fixup_syntax( if it.segment().is_none() { append.insert(colon.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }) @@ -333,7 +334,7 @@ pub(crate) fn fixup_syntax( if it.body().is_none() { append.insert(node.into(), vec![ Leaf::Ident(Ident { - text: "__ra_fixup".into(), + sym: sym::__ra_fixup.clone(), span: fake_span(node_range), is_raw: tt::IdentIsRaw::No }) @@ -448,9 +449,9 @@ mod tests { // `TokenTree`s, see the last assertion in `check()`. fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool { match (a, b) { - (tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text == b.text, + (tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.symbol == b.symbol, (tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char, - (tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.text == b.text, + (tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.sym == b.sym, _ => false, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs index 0c112554e1f43..ee15b1b5ce9d5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs @@ -10,6 +10,7 @@ use std::sync::OnceLock; +use intern::Symbol; use rustc_hash::FxHashMap; pub struct BuiltinAttribute { @@ -26,11 +27,16 @@ pub struct AttributeTemplate { pub name_value_str: Option<&'static str>, } -pub fn find_builtin_attr_idx(name: &str) -> Option { - static BUILTIN_LOOKUP_TABLE: OnceLock> = OnceLock::new(); +pub fn find_builtin_attr_idx(name: &Symbol) -> Option { + static BUILTIN_LOOKUP_TABLE: OnceLock> = OnceLock::new(); BUILTIN_LOOKUP_TABLE .get_or_init(|| { - INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect() + INERT_ATTRIBUTES + .iter() + .map(|attr| attr.name) + .enumerate() + .map(|(a, b)| (Symbol::intern(b), a)) + .collect() }) .get(name) .copied() diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 907e939153ba7..2c26fe414d91e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -316,30 +316,36 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option PathKind::Abs, _ => return None, }, - tt::Leaf::Ident(tt::Ident { text, span, .. }) if text == "$crate" => { + tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if *text == sym::dollar_crate => { resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate) } - tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::SELF, - tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => { + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF, + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => { let mut deg = 1; - while let Some(tt::Leaf::Ident(tt::Ident { text, span, is_raw })) = leaves.next() { - if text != "super" { - segments.push(Name::new(text, *is_raw, span.ctx)); + while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw })) = leaves.next() { + if *text != sym::super_ { + segments.push(Name::new_symbol_maybe_raw(text.clone(), *is_raw, span.ctx)); break; } deg += 1; } PathKind::Super(deg) } - tt::Leaf::Ident(tt::Ident { text, .. }) if text == "crate" => PathKind::Crate, + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate, tt::Leaf::Ident(ident) => { - segments.push(Name::new(&ident.text, ident.is_raw, ident.span.ctx)); + segments.push(Name::new_symbol_maybe_raw( + ident.sym.clone(), + ident.is_raw, + ident.span.ctx, + )); PathKind::Plain } _ => return None, }; segments.extend(leaves.filter_map(|leaf| match leaf { - ::tt::Leaf::Ident(ident) => Some(Name::new(&ident.text, ident.is_raw, ident.span.ctx)), + ::tt::Leaf::Ident(ident) => { + Some(Name::new_symbol_maybe_raw(ident.sym.clone(), ident.is_raw, ident.span.ctx)) + } _ => None, })); Some(ModPath { kind, segments }) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index fce9df6722b47..64c094bd28759 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -17,6 +17,8 @@ use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr}; pub struct Name { symbol: Symbol, ctx: (), + // FIXME: We should probably encode rawness as a property here instead, once we have hygiene + // in here we've got 4 bytes of padding to fill anyways } impl fmt::Debug for Name { @@ -187,14 +189,22 @@ impl Name { &self.symbol } - pub const fn new_symbol(doc: Symbol, ctx: SyntaxContextId) -> Self { + pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { _ = ctx; - Self { symbol: doc, ctx: () } + Self { symbol, ctx: () } + } + + pub fn new_symbol_maybe_raw(sym: Symbol, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Self { + if raw.no() { + Self { symbol: sym, ctx: () } + } else { + Name::new(sym.as_str(), raw, ctx) + } } // FIXME: This needs to go once we have hygiene - pub const fn new_symbol_root(doc: Symbol) -> Self { - Self { symbol: doc, ctx: () } + pub const fn new_symbol_root(sym: Symbol) -> Self { + Self { symbol: sym, ctx: () } } } @@ -250,7 +260,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(&self.text) + Name::resolve(self.sym.as_str()) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index f1d28450b31e8..a65d46161e583 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -1,14 +1,14 @@ //! A simplified version of quote-crate like quasi quote macro #![allow(clippy::crate_in_macro_def)] -use intern::Symbol; +use intern::{sym, Symbol}; use span::Span; use tt::IdentIsRaw; use crate::name::Name; -pub(crate) const fn dollar_crate(span: Span) -> tt::Ident { - tt::Ident { text: syntax::SmolStr::new_static("$crate"), span, is_raw: tt::IdentIsRaw::No } +pub(crate) fn dollar_crate(span: Span) -> tt::Ident { + tt::Ident { sym: sym::dollar_crate.clone(), span, is_raw: tt::IdentIsRaw::No } } // A helper macro quote macro @@ -99,7 +99,7 @@ macro_rules! __quote { ($span:ident $tt:ident ) => { vec![ { crate::tt::Leaf::Ident(crate::tt::Ident { - text: stringify!($tt).into(), + sym: intern::Symbol::intern(stringify!($tt)), span: $span, is_raw: tt::IdentIsRaw::No, }).into() @@ -177,12 +177,6 @@ impl ToTokenTree for crate::tt::TokenTree { } } -impl ToTokenTree for &crate::tt::TokenTree { - fn to_token(self, _: Span) -> crate::tt::TokenTree { - self.clone() - } -} - impl ToTokenTree for crate::tt::Subtree { fn to_token(self, _: Span) -> crate::tt::TokenTree { self.into() @@ -198,35 +192,34 @@ macro_rules! impl_to_to_tokentrees { leaf.into() } } - - impl ToTokenTree for &$ty { - fn to_token($this, $span: Span) -> crate::tt::TokenTree { - let leaf: crate::tt::Leaf = $im.clone().into(); - leaf.into() - } - } )* } } +impl ToTokenTree for &T { + fn to_token(self, span: Span) -> crate::tt::TokenTree { + self.clone().to_token(span) + } +} + impl_to_to_tokentrees! { - span: u32 => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; - span: usize => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; - span: i32 => self { crate::tt::Literal{text: self.to_string().into(), span, kind: tt::LitKind::Integer, suffix: None } }; - span: bool => self { crate::tt::Ident{text: self.to_string().into(), span, is_raw: tt::IdentIsRaw::No } }; + span: u32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } }; + span: usize => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } }; + span: i32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } }; + span: bool => self { crate::tt::Ident{sym: if self { sym::true_.clone() } else { sym::false_.clone() }, span, is_raw: tt::IdentIsRaw::No } }; _span: crate::tt::Leaf => self { self }; _span: crate::tt::Literal => self { self }; _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; - span: &str => self { crate::tt::Literal{text: (*self).into(), span, kind: tt::LitKind::Str, suffix: None }}; - span: String => self { crate::tt::Literal{text: self.into(), span, kind: tt::LitKind::Str, suffix: None }}; + span: &str => self { crate::tt::Literal{symbol: Symbol::intern(self), span, kind: tt::LitKind::Str, suffix: None }}; + span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self), span, kind: tt::LitKind::Str, suffix: None }}; span: Name => self { let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); - crate::tt::Ident{text: s.into(), span, is_raw } + crate::tt::Ident{sym: Symbol::intern(s), span, is_raw } }; span: Symbol => self { let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); - crate::tt::Ident{text: s.into(), span, is_raw } + crate::tt::Ident{sym: Symbol::intern(s), span, is_raw } }; } @@ -236,6 +229,7 @@ mod tests { use ::tt::IdentIsRaw; use base_db::FileId; use expect_test::expect; + use intern::Symbol; use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::{TextRange, TextSize}; @@ -268,7 +262,7 @@ mod tests { fn mk_ident(name: &str) -> crate::tt::Ident { let (is_raw, s) = IdentIsRaw::split_from_symbol(name); - crate::tt::Ident { text: s.into(), span: DUMMY, is_raw } + crate::tt::Ident { sym: Symbol::intern(s), span: DUMMY, is_raw } } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 01e43a67e434b..1374d0c38b866 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -3,6 +3,7 @@ use std::fmt; use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use intern::sym; use once_cell::unsync::Lazy; use rustc_index::IndexVec; use rustc_pattern_analysis::{ @@ -75,9 +76,9 @@ pub(crate) struct MatchCheckCtx<'db> { impl<'db> MatchCheckCtx<'db> { pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self { let def_map = db.crate_def_map(module.krate()); - let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns"); + let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); let min_exhaustive_patterns = - def_map.is_unstable_feature_enabled("min_exhaustive_patterns"); + def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns); Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index c868357ff982f..9f33c50670857 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3380,23 +3380,27 @@ impl BuiltinAttr { if let builtin @ Some(_) = Self::builtin(name) { return builtin; } - let idx = - db.crate_def_map(krate.id).registered_attrs().iter().position(|it| it == name)? as u32; + let idx = db + .crate_def_map(krate.id) + .registered_attrs() + .iter() + .position(|it| it.as_str() == name)? as u32; Some(BuiltinAttr { krate: Some(krate.id), idx }) } fn builtin(name: &str) -> Option { - hir_expand::inert_attr_macro::find_builtin_attr_idx(name) + hir_expand::inert_attr_macro::find_builtin_attr_idx(&Symbol::intern(name)) .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 }) } - pub fn name(&self, db: &dyn HirDatabase) -> SmolStr { - // FIXME: Return a `Name` here + pub fn name(&self, db: &dyn HirDatabase) -> Name { match self.krate { - Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), - None => { - SmolStr::new(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name) - } + Some(krate) => Name::new_symbol_root( + db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), + ), + None => Name::new_symbol_root(Symbol::intern( + hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, + )), } } @@ -3420,13 +3424,15 @@ impl ToolModule { pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { let krate = krate.id; let idx = - db.crate_def_map(krate).registered_tools().iter().position(|it| it == name)? as u32; + db.crate_def_map(krate).registered_tools().iter().position(|it| it.as_str() == name)? + as u32; Some(ToolModule { krate, idx }) } - pub fn name(&self, db: &dyn HirDatabase) -> SmolStr { - // FIXME: Return a `Name` here - db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone() + pub fn name(&self, db: &dyn HirDatabase) -> Name { + Name::new_symbol_root( + db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone(), + ) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index dbb2adcd301f5..a53c156b5da62 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -192,13 +192,13 @@ impl Definition { let AttributeTemplate { word, list, name_value_str } = it.template(db)?; let mut docs = "Valid forms are:".to_owned(); if word { - format_to!(docs, "\n - #\\[{}]", name); + format_to!(docs, "\n - #\\[{}]", name.display(db)); } if let Some(list) = list { - format_to!(docs, "\n - #\\[{}({})]", name, list); + format_to!(docs, "\n - #\\[{}({})]", name.display(db), list); } if let Some(name_value_str) = name_value_str { - format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str); + format_to!(docs, "\n - #\\[{} = {}]", name.display(db), name_value_str); } Some(Documentation::new(docs.replace('*', "\\*"))) } @@ -256,8 +256,8 @@ impl Definition { Definition::GenericParam(it) => it.display(db).to_string(), Definition::Label(it) => it.name(db).display(db).to_string(), Definition::ExternCrateDecl(it) => it.display(db).to_string(), - Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db)), - Definition::ToolModule(it) => it.name(db).to_string(), + Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db)), + Definition::ToolModule(it) => it.name(db).display(db).to_string(), Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)), } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index a3cc5c3d6a3d6..9e3f6d842f315 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -174,6 +174,32 @@ impl Symbol { } } + pub fn integer(i: usize) -> Self { + match i { + 0 => symbols::INTEGER_0.clone(), + 1 => symbols::INTEGER_1.clone(), + 2 => symbols::INTEGER_2.clone(), + 3 => symbols::INTEGER_3.clone(), + 4 => symbols::INTEGER_4.clone(), + 5 => symbols::INTEGER_5.clone(), + 6 => symbols::INTEGER_6.clone(), + 7 => symbols::INTEGER_7.clone(), + 8 => symbols::INTEGER_8.clone(), + 9 => symbols::INTEGER_9.clone(), + 10 => symbols::INTEGER_10.clone(), + 11 => symbols::INTEGER_11.clone(), + 12 => symbols::INTEGER_12.clone(), + 13 => symbols::INTEGER_13.clone(), + 14 => symbols::INTEGER_14.clone(), + 15 => symbols::INTEGER_15.clone(), + i => Symbol::intern(&format!("{i}")), + } + } + + pub fn empty() -> Self { + symbols::__empty.clone() + } + pub fn as_str(&self) -> &str { self.repr.as_str() } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 064335471e95a..61b9d099e06a3 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -56,6 +56,10 @@ macro_rules! define_symbols { define_symbols! { @WITH_NAME: + __empty = "", + unsafe_ = "unsafe", + in_ = "in", + super_ = "super", self_ = "self", Self_ = "Self", tick_static = "'static", @@ -78,10 +82,18 @@ define_symbols! { INTEGER_14 = "14", INTEGER_15 = "15", fn_ = "fn", + crate_ = "crate", + underscore = "_", + true_ = "true", + false_ = "false", + let_ = "let", + const_ = "const", @PLAIN: + __ra_fixup, add_assign, add, + attributes, align_offset, alloc_layout, alloc, @@ -92,6 +104,9 @@ define_symbols! { bench, bitand_assign, bitand, + notable_trait, + hidden, + local_inner_macros, bitor_assign, bitor, bitxor_assign, @@ -225,9 +240,12 @@ define_symbols! { log_syntax, lt, macro_rules, + ignore, + count, manually_drop, maybe_uninit, metadata_type, + missing, module_path, mul_assign, mul, @@ -349,6 +367,10 @@ define_symbols! { u8, Unknown, unpin, + simd, + C, + align, + packed, unreachable_2015, unreachable_2021, unreachable, @@ -356,5 +378,7 @@ define_symbols! { unsize, usize, v1, + exhaustive_patterns, + min_exhaustive_patterns, va_list } diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 1002de2104a0c..57834623e84fa 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -24,6 +24,7 @@ parser.workspace = true tt.workspace = true stdx.workspace = true span.workspace = true +intern.workspace = true [dev-dependencies] test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 6a2f1c236869d..04e78a0833407 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -1,10 +1,11 @@ //! This module add real world mbe example for benchmark tests +use intern::Symbol; use rustc_hash::FxHashMap; use span::{Edition, Span}; use syntax::{ ast::{self, HasName}, - AstNode, SmolStr, + AstNode, }; use test_utils::{bench, bench_fixture, skip_slow_tests}; @@ -228,7 +229,7 @@ fn invocation_fixtures( fn make_ident(ident: &str) -> tt::TokenTree { tt::Leaf::Ident(tt::Ident { span: DUMMY, - text: SmolStr::new(ident), + sym: Symbol::intern(ident), is_raw: tt::IdentIsRaw::No, }) .into() @@ -239,7 +240,7 @@ fn invocation_fixtures( fn make_literal(lit: &str) -> tt::TokenTree { tt::Leaf::Literal(tt::Literal { span: DUMMY, - text: SmolStr::new(lit), + symbol: Symbol::intern(lit), kind: tt::LitKind::Str, suffix: None, }) diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index cfad8bcc0b454..55b89aa848d5d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -5,9 +5,9 @@ mod matcher; mod transcriber; +use intern::Symbol; use rustc_hash::FxHashMap; use span::{Edition, Span}; -use syntax::SmolStr; use crate::{parser::MetaVarKind, ExpandError, ExpandResult, MatchedArmIndex}; @@ -110,12 +110,12 @@ pub(crate) fn expand_rules( /// the `Bindings` we should take. We push to the stack when we enter a /// repetition. /// -/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to +/// In other words, `Bindings` is a *multi* mapping from `Symbol` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. #[derive(Debug, Default, Clone, PartialEq, Eq)] struct Bindings { - inner: FxHashMap, + inner: FxHashMap, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index b20d5579ca63b..3762d20bab62e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -61,9 +61,9 @@ use std::{rc::Rc, sync::Arc}; +use intern::{sym, Symbol}; use smallvec::{smallvec, SmallVec}; use span::{Edition, Span}; -use syntax::SmolStr; use tt::{iter::TtIter, DelimSpan}; use crate::{ @@ -74,12 +74,12 @@ use crate::{ }; impl Bindings { - fn push_optional(&mut self, name: &SmolStr) { - self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty)); + fn push_optional(&mut self, name: Symbol) { + self.inner.insert(name, Binding::Fragment(Fragment::Empty)); } - fn push_empty(&mut self, name: &SmolStr) { - self.inner.insert(name.clone(), Binding::Empty); + fn push_empty(&mut self, name: Symbol) { + self.inner.insert(name, Binding::Empty); } fn bindings(&self) -> impl Iterator { @@ -127,10 +127,10 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition: #[derive(Debug, Clone)] enum BindingKind { - Empty(SmolStr), - Optional(SmolStr), - Fragment(SmolStr, Fragment), - Missing(SmolStr, MetaVarKind), + Empty(Symbol), + Optional(Symbol), + Fragment(Symbol, Fragment), + Missing(Symbol, MetaVarKind), Nested(usize, usize), } @@ -178,20 +178,20 @@ impl BindingsBuilder { } } - fn push_empty(&mut self, idx: &mut BindingsIdx, var: &SmolStr) { + fn push_empty(&mut self, idx: &mut BindingsIdx, var: &Symbol) { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Empty(var.clone())))); } - fn push_optional(&mut self, idx: &mut BindingsIdx, var: &SmolStr) { + fn push_optional(&mut self, idx: &mut BindingsIdx, var: &Symbol) { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone())))); } - fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } - fn push_missing(&mut self, idx: &mut BindingsIdx, var: &SmolStr, kind: MetaVarKind) { + fn push_missing(&mut self, idx: &mut BindingsIdx, var: &Symbol, kind: MetaVarKind) { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Missing(var.clone(), kind)))); } @@ -219,10 +219,10 @@ impl BindingsBuilder { for cmd in nodes { match cmd { BindingKind::Empty(name) => { - bindings.push_empty(name); + bindings.push_empty(name.clone()); } BindingKind::Optional(name) => { - bindings.push_optional(name); + bindings.push_optional(name.clone()); } BindingKind::Fragment(name, fragment) => { bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone())); @@ -507,7 +507,7 @@ fn match_loop_inner<'t>( } OpDelimited::Op(Op::Literal(lhs)) => { if let Ok(rhs) = src.clone().expect_leaf() { - if matches!(rhs, tt::Leaf::Literal(it) if it.text == lhs.text) { + if matches!(rhs, tt::Leaf::Literal(it) if it.symbol == lhs.symbol) { item.dot.next(); } else { res.add_err(ExpandError::UnexpectedToken); @@ -521,7 +521,7 @@ fn match_loop_inner<'t>( } OpDelimited::Op(Op::Ident(lhs)) => { if let Ok(rhs) = src.clone().expect_leaf() { - if matches!(rhs, tt::Leaf::Ident(it) if it.text == lhs.text) { + if matches!(rhs, tt::Leaf::Ident(it) if it.sym == lhs.sym) { item.dot.next(); } else { res.add_err(ExpandError::UnexpectedToken); @@ -554,7 +554,7 @@ fn match_loop_inner<'t>( // ident, not a punct. ExpandError::UnexpectedToken } else { - let lhs: SmolStr = lhs.collect(); + let lhs = lhs.collect::(); ExpandError::binding_error(format!("expected punct: `{lhs}`")) } } else { @@ -759,7 +759,9 @@ fn match_meta_var( // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576 match input.peek_n(0) { Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) - if it.text == "_" || it.text == "let" || it.text == "const" => + if it.sym == sym::underscore + || it.sym == sym::let_ + || it.sym == sym::const_ => { return ExpandResult::only_err(ExpandError::NoMatchingRule) } @@ -824,7 +826,7 @@ fn match_meta_var( expect_fragment(input, fragment, edition).map(|it| it.map(Fragment::Tokens)) } -fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { +fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) { for op in pattern.iter() { match op { Op::Var { name, .. } => collector_fun(name.clone()), @@ -908,13 +910,13 @@ fn expect_separator(iter: &mut TtIter<'_, S>, separator: &Separator) -> let mut fork = iter.clone(); let ok = match separator { Separator::Ident(lhs) => match fork.expect_ident_or_underscore() { - Ok(rhs) => rhs.text == lhs.text, + Ok(rhs) => rhs.sym == lhs.sym, Err(_) => false, }, Separator::Literal(lhs) => match fork.expect_literal() { Ok(rhs) => match rhs { - tt::Leaf::Literal(rhs) => rhs.text == lhs.text, - tt::Leaf::Ident(rhs) => rhs.text == lhs.text, + tt::Leaf::Literal(rhs) => rhs.symbol == lhs.symbol, + tt::Leaf::Ident(rhs) => rhs.sym == lhs.symbol, tt::Leaf::Punct(_) => false, }, Err(_) => false, diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index e3359865cb276..4de7cccd9680e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -1,8 +1,8 @@ //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` +use intern::{sym, Symbol}; use span::Span; -use syntax::{format_smolstr, SmolStr}; use tt::Delimiter; use crate::{ @@ -12,16 +12,16 @@ use crate::{ }; impl Bindings { - fn get(&self, name: &str) -> Result<&Binding, ExpandError> { + fn get(&self, name: &Symbol) -> Result<&Binding, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), - None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name)))), + None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name.as_str())))), } } fn get_fragment( &self, - name: &str, + name: &Symbol, mut span: Span, nesting: &mut [NestingState], marker: impl Fn(&mut Span), @@ -97,7 +97,7 @@ impl Bindings { | MetaVarKind::Expr | MetaVarKind::Ident => { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: SmolStr::new_static("missing"), + sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, }))) @@ -112,7 +112,7 @@ impl Bindings { spacing: tt::Spacing::Joint, })), tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: SmolStr::new_static("missing"), + sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, })), @@ -121,7 +121,7 @@ impl Bindings { } MetaVarKind::Literal => { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: SmolStr::new_static("\"missing\""), + sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, }))) @@ -239,7 +239,7 @@ fn expand_subtree( ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); arena.push( tt::Leaf::Literal(tt::Literal { - text: format_smolstr!("{index}"), + symbol: Symbol::integer(index), span: ctx.call_site, kind: tt::LitKind::Integer, suffix: None, @@ -254,7 +254,7 @@ fn expand_subtree( }); arena.push( tt::Leaf::Literal(tt::Literal { - text: format_smolstr!("{length}"), + symbol: Symbol::integer(length), span: ctx.call_site, kind: tt::LitKind::Integer, suffix: None, @@ -263,7 +263,7 @@ fn expand_subtree( ); } Op::Count { name, depth } => { - let mut binding = match ctx.bindings.get(name.as_str()) { + let mut binding = match ctx.bindings.get(name) { Ok(b) => b, Err(e) => { if err.is_none() { @@ -321,7 +321,7 @@ fn expand_subtree( }; arena.push( tt::Leaf::Literal(tt::Literal { - text: format_smolstr!("{c}"), + symbol: Symbol::integer(c), span: ctx.call_site, suffix: None, kind: tt::LitKind::Integer, @@ -344,12 +344,12 @@ fn expand_subtree( fn expand_var( ctx: &mut ExpandCtx<'_>, - v: &SmolStr, + v: &Symbol, id: Span, marker: impl Fn(&mut Span), ) -> ExpandResult { // We already handle $crate case in mbe parser - debug_assert!(v != "crate"); + debug_assert!(*v != sym::crate_); match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) { Ok(it) => ExpandResult::ok(it), @@ -373,7 +373,7 @@ fn expand_var( tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }) .into(), tt::Leaf::from(tt::Ident { - text: v.clone(), + sym: v.clone(), span: id, is_raw: tt::IdentIsRaw::No, }) diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index 18af35c1e2900..74a2c771e2037 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -4,8 +4,8 @@ use std::sync::Arc; use arrayvec::ArrayVec; +use intern::{sym, Symbol}; use span::{Edition, Span, SyntaxContextId}; -use syntax::SmolStr; use tt::iter::TtIter; use crate::ParseError; @@ -67,12 +67,12 @@ impl MetaTemplate { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum Op { Var { - name: SmolStr, + name: Symbol, kind: Option, id: Span, }, Ignore { - name: SmolStr, + name: Symbol, id: Span, }, Index { @@ -82,7 +82,7 @@ pub(crate) enum Op { depth: usize, }, Count { - name: SmolStr, + name: Symbol, // FIXME: `usize`` once we drop support for 1.76 depth: Option, }, @@ -138,8 +138,8 @@ impl PartialEq for Separator { use Separator::*; match (self, other) { - (Ident(a), Ident(b)) => a.text == b.text, - (Literal(a), Literal(b)) => a.text == b.text, + (Ident(a), Ident(b)) => a.sym == b.sym, + (Literal(a), Literal(b)) => a.symbol == b.symbol, (Puncts(a), Puncts(b)) if a.len() == b.len() => { let a_iter = a.iter().map(|a| a.char); let b_iter = b.iter().map(|b| b.char); @@ -203,23 +203,23 @@ fn next_op( } }, tt::TokenTree::Leaf(leaf) => match leaf { - tt::Leaf::Ident(ident) if ident.text == "crate" => { + tt::Leaf::Ident(ident) if ident.sym == sym::crate_ => { // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. Op::Ident(tt::Ident { - text: "$crate".into(), + sym: sym::dollar_crate.clone(), span: ident.span, is_raw: tt::IdentIsRaw::No, }) } tt::Leaf::Ident(ident) => { let kind = eat_fragment_kind(edition, src, mode)?; - let name = ident.text.clone(); + let name = ident.sym.clone(); let id = ident.span; Op::Var { name, kind, id } } tt::Leaf::Literal(lit) if is_boolean_literal(lit) => { let kind = eat_fragment_kind(edition, src, mode)?; - let name = lit.text.clone(); + let name = lit.symbol.clone(); let id = lit.span; Op::Var { name, kind, id } } @@ -277,7 +277,7 @@ fn eat_fragment_kind( let ident = src .expect_ident() .map_err(|()| ParseError::unexpected("missing fragment specifier"))?; - let kind = match ident.text.as_str() { + let kind = match ident.sym.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, "pat" => match edition(ident.span.ctx) { @@ -303,7 +303,7 @@ fn eat_fragment_kind( } fn is_boolean_literal(lit: &tt::Literal) -> bool { - matches!(lit.text.as_str(), "true" | "false") + matches!(lit.symbol.as_str(), "true" | "false") } fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, RepeatKind), ParseError> { @@ -353,23 +353,23 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result let mut args = TtIter::new(args); - let op = match &*func.text { - "ignore" => { + let op = match &func.sym { + s if sym::ignore == *s => { if new_meta_vars { args.expect_dollar()?; } let ident = args.expect_ident()?; - Op::Ignore { name: ident.text.clone(), id: ident.span } + Op::Ignore { name: ident.sym.clone(), id: ident.span } } - "index" => Op::Index { depth: parse_depth(&mut args)? }, - "len" => Op::Len { depth: parse_depth(&mut args)? }, - "count" => { + s if sym::index == *s => Op::Index { depth: parse_depth(&mut args)? }, + s if sym::len == *s => Op::Len { depth: parse_depth(&mut args)? }, + s if sym::count == *s => { if new_meta_vars { args.expect_dollar()?; } let ident = args.expect_ident()?; let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; - Op::Count { name: ident.text.clone(), depth } + Op::Count { name: ident.sym.clone(), depth } } _ => return Err(()), }; @@ -384,11 +384,11 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { if src.len() == 0 { Ok(0) - } else if let tt::Leaf::Literal(tt::Literal { text, suffix: None, .. }) = + } else if let tt::Leaf::Literal(tt::Literal { symbol: text, suffix: None, .. }) = src.expect_literal()? { // Suffixes are not allowed. - text.parse().map_err(|_| ()) + text.as_str().parse().map_err(|_| ()) } else { Err(()) } diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 4d66464932b88..7ff89631080e4 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -2,6 +2,7 @@ use std::fmt; +use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, SpanAnchor, SpanData, SpanMap}; use stdx::{format_to, never, non_empty_vec::NonEmptyVec}; @@ -322,7 +323,7 @@ where () => { tt::Ident { span: conv.span_for(abs_range), - text: token.to_text(conv), + sym: Symbol::intern(&token.to_text(conv)), is_raw: tt::IdentIsRaw::No, } .into() @@ -332,14 +333,14 @@ where T![true] | T![false] => make_ident!(), IDENT => { let text = token.to_text(conv); - tt::Ident::new(text, conv.span_for(abs_range)).into() + tt::Ident::new(&text, conv.span_for(abs_range)).into() } UNDERSCORE => make_ident!(), k if k.is_keyword() => make_ident!(), k if k.is_literal() => { let text = token.to_text(conv); let span = conv.span_for(abs_range); - token_to_literal(text, span).into() + token_to_literal(&text, span).into() } LIFETIME_IDENT => { let apostrophe = tt::Leaf::from(tt::Punct { @@ -351,7 +352,7 @@ where token_trees.push(apostrophe.into()); let ident = tt::Leaf::from(tt::Ident { - text: SmolStr::new(&token.to_text(conv)[1..]), + sym: Symbol::intern(&token.to_text(conv)[1..]), span: conv.span_for(TextRange::new( abs_range.start() + TextSize::of('\''), abs_range.end(), @@ -436,7 +437,7 @@ fn is_single_token_op(kind: SyntaxKind) -> bool { /// And then quote the string, which is needed to convert to `tt::Literal` /// /// Note that proc-macros desugar with string literals where as macro_rules macros desugar with raw string literals. -pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (SmolStr, tt::LitKind) { +pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (Symbol, tt::LitKind) { match mode { DocCommentDesugarMode::Mbe => { let mut num_of_hashes = 0; @@ -451,11 +452,11 @@ pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (Smo } // Quote raw string with delimiters - (text.into(), tt::LitKind::StrRaw(num_of_hashes)) + (Symbol::intern(text), tt::LitKind::StrRaw(num_of_hashes)) } // Quote string with delimiters DocCommentDesugarMode::ProcMacro => { - (format_smolstr!("{}", text.escape_debug()), tt::LitKind::Str) + (Symbol::intern(&format_smolstr!("{}", text.escape_debug())), tt::LitKind::Str) } } } @@ -471,7 +472,7 @@ fn convert_doc_comment( let mk_ident = |s: &str| { tt::TokenTree::from(tt::Leaf::from(tt::Ident { - text: s.into(), + sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No, })) @@ -494,7 +495,7 @@ fn convert_doc_comment( text = &text[0..text.len() - 2]; } let (text, kind) = desugar_doc_comment_text(text, mode); - let lit = tt::Literal { text, span, kind, suffix: None }; + let lit = tt::Literal { symbol: text, span, kind, suffix: None }; tt::TokenTree::from(tt::Leaf::from(lit)) }; @@ -928,7 +929,12 @@ where fn float_split(&mut self, has_pseudo_dot: bool) { let (text, span) = match self.cursor.token_tree() { Some(tt::buffer::TokenTreeRef::Leaf( - tt::Leaf::Literal(tt::Literal { text, span, kind: tt::LitKind::Float, suffix: _ }), + tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::Float, + suffix: _, + }), _, )) => (text.as_str(), *span), tt => unreachable!("{tt:?}"), @@ -988,7 +994,7 @@ where self.buf.push_str("r#"); self.text_pos += TextSize::of("r#"); } - let r = (ident.text.as_str(), ident.span); + let r = (ident.sym.as_str(), ident.span); self.cursor = self.cursor.bump(); r } diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs index bf5494d37149b..7636359805c72 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs @@ -49,23 +49,22 @@ pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) }; res.push(kind); - if kind == FLOAT_NUMBER && !lit.text.ends_with('.') { + if kind == FLOAT_NUMBER && !lit.symbol.as_str().ends_with('.') { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access res.was_joint(); } } - tt::Leaf::Ident(ident) => match ident.text.as_ref() { + tt::Leaf::Ident(ident) => match ident.sym.as_str() { "_" => res.push(T![_]), i if i.starts_with('\'') => res.push(LIFETIME_IDENT), _ if ident.is_raw.yes() => res.push(IDENT), - _ => match SyntaxKind::from_keyword(&ident.text) { + text => match SyntaxKind::from_keyword(text) { Some(kind) => res.push(kind), None => { - let contextual_keyword = - SyntaxKind::from_contextual_keyword(&ident.text) - .unwrap_or(SyntaxKind::IDENT); + let contextual_keyword = SyntaxKind::from_contextual_keyword(text) + .unwrap_or(SyntaxKind::IDENT); res.push_ident(contextual_keyword); } }, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 889eefa8b5c61..345fb9f8ae973 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -28,8 +28,7 @@ span.workspace = true # InternIds for the syntax context base-db.workspace = true la-arena.workspace = true -# only here to parse via token_to_literal -mbe.workspace = true +intern.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 3a915e668bbff..54c1475b8b1c8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -13,7 +13,6 @@ use base_db::Env; use paths::{AbsPath, AbsPathBuf}; use span::Span; use std::{fmt, io, sync::Arc}; -use tt::SmolStr; use serde::{Deserialize, Serialize}; @@ -66,7 +65,7 @@ impl MacroDylib { pub struct ProcMacro { process: Arc, dylib_path: Arc, - name: SmolStr, + name: Box, kind: ProcMacroKind, } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index 65835048173ab..65f60a7c5bd28 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -158,6 +158,7 @@ type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) #[cfg(test)] mod tests { use base_db::FileId; + use intern::{sym, Symbol}; use la_arena::RawIdx; use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId}; use text_size::{TextRange, TextSize}; @@ -174,7 +175,7 @@ mod tests { let token_trees = Box::new([ TokenTree::Leaf( Ident { - text: "struct".into(), + sym: Symbol::intern("struct"), span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, @@ -186,7 +187,7 @@ mod tests { ), TokenTree::Leaf( Ident { - text: "Foo".into(), + sym: Symbol::intern("Foo"), span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, @@ -197,7 +198,7 @@ mod tests { .into(), ), TokenTree::Leaf(Leaf::Literal(Literal { - text: "Foo".into(), + symbol: Symbol::intern("Foo"), span: Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, @@ -230,14 +231,14 @@ mod tests { kind: DelimiterKind::Brace, }, token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal { - text: "0".into(), + symbol: sym::INTEGER_0.clone(), span: Span { range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), anchor, ctx: SyntaxContextId::ROOT, }, kind: tt::LitKind::Integer, - suffix: Some(Box::new("u32".into())), + suffix: Some(sym::u32.clone()), }))]), }), ]); diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index 3d962e99d92c2..19260bc817dae 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -37,6 +37,7 @@ use std::collections::VecDeque; +use intern::Symbol; use la_arena::RawIdx; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -433,8 +434,8 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { let id = self.token_id_of(lit.span); let (text, suffix) = if self.version >= EXTENDED_LEAF_DATA { ( - self.intern(&lit.text), - lit.suffix.as_ref().map(|s| self.intern(s)).unwrap_or(!0), + self.intern(lit.symbol.as_str()), + lit.suffix.as_ref().map(|s| self.intern(s.as_str())).unwrap_or(!0), ) } else { (self.intern_owned(format!("{lit}")), !0) @@ -469,11 +470,11 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> { let idx = self.ident.len() as u32; let id = self.token_id_of(ident.span); let text = if self.version >= EXTENDED_LEAF_DATA { - self.intern(&ident.text) + self.intern(ident.sym.as_str()) } else if ident.is_raw.yes() { - self.intern_owned(format!("r#{}", ident.text,)) + self.intern_owned(format!("r#{}", ident.sym.as_str(),)) } else { - self.intern(&ident.text) + self.intern(ident.sym.as_str()) }; self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() }); idx << 2 | 0b11 @@ -555,7 +556,7 @@ impl<'span, S: InternableSpan> Reader<'span, S> { let span = read_span(repr.id); tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { tt::Literal { - text: text.into(), + symbol: Symbol::intern(text), span, kind: match u16::to_le_bytes(repr.kind) { [0, _] => Err(()), @@ -572,15 +573,15 @@ impl<'span, S: InternableSpan> Reader<'span, S> { _ => unreachable!(), }, suffix: if repr.suffix != !0 { - Some(Box::new( - self.text[repr.suffix as usize].as_str().into(), + Some(Symbol::intern( + self.text[repr.suffix as usize].as_str(), )) } else { None }, } } else { - tt::token_to_literal(text.into(), span) + tt::token_to_literal(text, span) }) .into() } @@ -609,7 +610,7 @@ impl<'span, S: InternableSpan> Reader<'span, S> { tt::IdentIsRaw::split_from_symbol(text) }; tt::Leaf::Ident(tt::Ident { - text: text.into(), + sym: Symbol::intern(text), span: read_span(repr.id), is_raw, }) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 065701c05cc7b..673b5bd78a8e9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -25,6 +25,7 @@ base-db.workspace = true span.workspace = true proc-macro-api.workspace = true ra-ap-rustc_lexer.workspace = true +intern.workspace = true [dev-dependencies] expect-test = "1.4.0" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index e6281035e1a2e..f0aa6b3f93f95 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -130,14 +130,13 @@ impl ProcMacroSrvSpan for TokenId { type Server = server_impl::token_id::TokenIdServer; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server { - Self::Server { interner: &server_impl::SYMBOL_INTERNER, call_site, def_site, mixed_site } + Self::Server { call_site, def_site, mixed_site } } } impl ProcMacroSrvSpan for Span { type Server = server_impl::rust_analyzer_span::RaSpanServer; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server { Self::Server { - interner: &server_impl::SYMBOL_INTERNER, call_site, def_site, mixed_site, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs index 68e0f85978e0c..c9a8621690552 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs @@ -14,9 +14,9 @@ mod token_stream; pub use token_stream::TokenStream; pub mod rust_analyzer_span; -mod symbol; +// mod symbol; pub mod token_id; -pub use symbol::*; +// pub use symbol::*; use tt::Spacing; fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 1a71f39612d3b..452ad8e083daa 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -10,13 +10,14 @@ use std::{ ops::{Bound, Range}, }; +use intern::Symbol; use proc_macro::bridge::{self, server}; use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; use tt::{TextRange, TextSize}; use crate::server_impl::{ delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER, + token_stream::TokenStreamBuilder, }; mod tt { pub use tt::*; @@ -36,7 +37,6 @@ pub struct SourceFile; pub struct FreeFunctions; pub struct RaSpanServer { - pub(crate) interner: SymbolInternerRef, // FIXME: Report this back to the caller to track as dependencies pub tracked_env_vars: HashMap, Option>>, // FIXME: Report this back to the caller to track as dependencies @@ -126,15 +126,10 @@ impl server::FreeFunctions for RaSpanServer { let lit = &lit[start_offset..lit.len() - end_offset]; let suffix = match suffix { "" | "_" => None, - suffix => Some(Symbol::intern(self.interner, suffix)), + suffix => Some(Symbol::intern(suffix)), }; - Ok(bridge::Literal { - kind, - symbol: Symbol::intern(self.interner, lit), - suffix, - span: self.call_site, - }) + Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site }) } fn emit_diagnostic(&mut self, _: bridge::Diagnostic) { @@ -170,9 +165,9 @@ impl server::TokenStream for RaSpanServer { } bridge::TokenTree::Ident(ident) => { - let text = ident.sym.text(self.interner); + let text = ident.sym; let ident: tt::Ident = tt::Ident { - text, + sym: text, span: ident.span, is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, }; @@ -183,8 +178,8 @@ impl server::TokenStream for RaSpanServer { bridge::TokenTree::Literal(literal) => { let literal = tt::Literal { - text: literal.symbol.text(self.interner), - suffix: literal.suffix.map(|it| Box::new(it.text(self.interner))), + symbol: literal.symbol, + suffix: literal.suffix, span: literal.span, kind: literal_kind_to_internal(literal.kind), }; @@ -255,7 +250,7 @@ impl server::TokenStream for RaSpanServer { .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(self.interner, &ident.text), + sym: ident.sym, is_raw: ident.is_raw.yes(), span: ident.span, }) @@ -264,8 +259,8 @@ impl server::TokenStream for RaSpanServer { bridge::TokenTree::Literal(bridge::Literal { span: lit.span, kind: literal_kind_to_external(lit.kind), - symbol: Symbol::intern(self.interner, &lit.text), - suffix: lit.suffix.map(|it| Symbol::intern(self.interner, &it)), + symbol: lit.symbol, + suffix: lit.suffix, }) } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { @@ -464,12 +459,95 @@ impl server::Server for RaSpanServer { } fn intern_symbol(ident: &str) -> Self::Symbol { - // FIXME: should be `self.interner` once the proc-macro api allows it. - Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) + Symbol::intern(ident) } fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - // FIXME: should be `self.interner` once the proc-macro api allows it. - f(symbol.text(&SYMBOL_INTERNER).as_str()) + f(symbol.as_str()) + } +} + +#[cfg(test)] +mod tests { + use span::SyntaxContextId; + + use super::*; + + #[test] + fn test_ra_server_to_string() { + let span = Span { + range: TextRange::empty(TextSize::new(0)), + anchor: span::SpanAnchor { + file_id: span::FileId::from_raw(0), + ast_id: span::ErasedFileAstId::from_raw(0.into()), + }, + ctx: SyntaxContextId::ROOT, + }; + let s = TokenStream { + token_trees: vec![ + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("struct"), + span, + is_raw: tt::IdentIsRaw::No, + })), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("T"), + span: span, + is_raw: tt::IdentIsRaw::No, + })), + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: span, + close: span, + kind: tt::DelimiterKind::Brace, + }, + token_trees: Box::new([]), + }), + ], + }; + + assert_eq!(s.to_string(), "struct T {}"); + } + + #[test] + fn test_ra_server_from_str() { + let span = Span { + range: TextRange::empty(TextSize::new(0)), + anchor: span::SpanAnchor { + file_id: span::FileId::from_raw(0), + ast_id: span::ErasedFileAstId::from_raw(0.into()), + }, + ctx: SyntaxContextId::ROOT, + }; + let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: span, + close: span, + kind: tt::DelimiterKind::Parenthesis, + }, + token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + is_raw: tt::IdentIsRaw::No, + sym: Symbol::intern("a"), + span, + }))]), + }); + + let t1 = TokenStream::from_str("(a)", span).unwrap(); + assert_eq!(t1.token_trees.len(), 1); + assert_eq!(t1.token_trees[0], subtree_paren_a); + + let t2 = TokenStream::from_str("(a);", span).unwrap(); + assert_eq!(t2.token_trees.len(), 2); + assert_eq!(t2.token_trees[0], subtree_paren_a); + + let underscore = TokenStream::from_str("_", span).unwrap(); + assert_eq!( + underscore.token_trees[0], + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("_"), + span, + is_raw: tt::IdentIsRaw::No, + })) + ); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs index 540d06457f2f3..6863ce959973e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs @@ -1,7 +1,6 @@ //! Symbol interner for proc-macro-srv use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; -use tt::SmolStr; thread_local! { pub(crate) static SYMBOL_INTERNER: RefCell = Default::default(); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 94d5748b0870f..7720c6d83c38d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -5,11 +5,12 @@ use std::{ ops::{Bound, Range}, }; +use intern::Symbol; use proc_macro::bridge::{self, server}; use crate::server_impl::{ delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER, + token_stream::TokenStreamBuilder, }; mod tt { pub use proc_macro_api::msg::TokenId; @@ -36,7 +37,6 @@ pub struct SourceFile; pub struct FreeFunctions; pub struct TokenIdServer { - pub(crate) interner: SymbolInternerRef, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, @@ -117,15 +117,10 @@ impl server::FreeFunctions for TokenIdServer { let lit = &lit[start_offset..lit.len() - end_offset]; let suffix = match suffix { "" | "_" => None, - suffix => Some(Symbol::intern(self.interner, suffix)), + suffix => Some(Symbol::intern(suffix)), }; - Ok(bridge::Literal { - kind, - symbol: Symbol::intern(self.interner, lit), - suffix, - span: self.call_site, - }) + Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site }) } fn emit_diagnostic(&mut self, _: bridge::Diagnostic) {} @@ -159,9 +154,8 @@ impl server::TokenStream for TokenIdServer { } bridge::TokenTree::Ident(ident) => { - let text = ident.sym.text(self.interner); let ident: tt::Ident = tt::Ident { - text, + sym: ident.sym, span: ident.span, is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, }; @@ -172,8 +166,8 @@ impl server::TokenStream for TokenIdServer { bridge::TokenTree::Literal(literal) => { let literal = Literal { - text: literal.symbol.text(self.interner), - suffix: literal.suffix.map(|it| Box::new(it.text(self.interner))), + symbol: literal.symbol, + suffix: literal.suffix, span: literal.span, kind: literal_kind_to_internal(literal.kind), }; @@ -239,7 +233,7 @@ impl server::TokenStream for TokenIdServer { .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(self.interner, &ident.text), + sym: ident.sym, is_raw: ident.is_raw.yes(), span: ident.span, }) @@ -248,8 +242,8 @@ impl server::TokenStream for TokenIdServer { bridge::TokenTree::Literal(bridge::Literal { span: lit.span, kind: literal_kind_to_external(lit.kind), - symbol: Symbol::intern(self.interner, &lit.text), - suffix: lit.suffix.map(|it| Symbol::intern(self.interner, &it)), + symbol: lit.symbol, + suffix: lit.suffix, }) } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { @@ -366,11 +360,11 @@ impl server::Server for TokenIdServer { } fn intern_symbol(ident: &str) -> Self::Symbol { - Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) + Symbol::intern(ident) } fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.text(&SYMBOL_INTERNER).as_str()) + f(symbol.as_str()) } } @@ -383,12 +377,12 @@ mod tests { let s = TokenStream { token_trees: vec![ tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "struct".into(), + sym: Symbol::intern("struct"), span: tt::TokenId(0), is_raw: tt::IdentIsRaw::No, })), tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "T".into(), + sym: Symbol::intern("T"), span: tt::TokenId(0), is_raw: tt::IdentIsRaw::No, })), @@ -416,7 +410,7 @@ mod tests { }, token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, - text: "a".into(), + sym: Symbol::intern("a"), span: tt::TokenId(0), }))]), }); @@ -433,7 +427,7 @@ mod tests { assert_eq!( underscore.token_trees[0], tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "_".into(), + sym: Symbol::intern("_"), span: tt::TokenId(0), is_raw: tt::IdentIsRaw::No, })) diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml index 35e39229894f2..f9565721dd5f9 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml @@ -16,6 +16,7 @@ base-db.workspace = true rustc-hash.workspace = true span.workspace = true stdx.workspace = true +intern.workspace = true [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index e65186d377151..7a0c474b750b6 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -13,6 +13,7 @@ use hir_expand::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacros, }, }; +use intern::Symbol; use rustc_hash::FxHashMap; use span::{Edition, FileId, FilePosition, FileRange, Span}; use test_utils::{ @@ -640,11 +641,11 @@ impl ProcMacroExpander for ShortenProcMacroExpander { Leaf::Literal(it) => { // XXX Currently replaces any literals with an empty string, but supporting // "shortening" other literals would be nice. - it.text = "\"\"".into(); + it.symbol = Symbol::empty(); } Leaf::Punct(_) => {} Leaf::Ident(it) => { - it.text = it.text.chars().take(1).collect(); + it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::()); } } leaf diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 1900635b995ee..cea1519c2dd46 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -13,10 +13,10 @@ doctest = false [dependencies] arrayvec.workspace = true -smol_str.workspace = true text-size.workspace = true stdx.workspace = true +intern.workspace = true ra-ap-rustc_lexer.workspace = true [features] diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index 175259a3e4719..c0195b835ac6c 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -2,6 +2,7 @@ //! macro definition into a list of patterns and templates. use arrayvec::ArrayVec; +use intern::sym; use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree}; @@ -58,7 +59,7 @@ impl<'a, S: Copy> TtIter<'a, S> { pub fn expect_ident(&mut self) -> Result<&'a Ident, ()> { match self.expect_leaf()? { - Leaf::Ident(it) if it.text != "_" => Ok(it), + Leaf::Ident(it) if it.sym != sym::underscore => Ok(it), _ => Err(()), } } @@ -74,7 +75,7 @@ impl<'a, S: Copy> TtIter<'a, S> { let it = self.expect_leaf()?; match it { Leaf::Literal(_) => Ok(it), - Leaf::Ident(ident) if ident.text == "true" || ident.text == "false" => Ok(it), + Leaf::Ident(ident) if ident.sym == sym::true_ || ident.sym == sym::false_ => Ok(it), _ => Err(()), } } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index c328b3f8a3cc8..7b72f9ff108d3 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -14,16 +14,16 @@ pub mod iter; use std::fmt; +use intern::Symbol; use stdx::{impl_from, itertools::Itertools as _}; -pub use smol_str::{format_smolstr, SmolStr}; pub use text_size::{TextRange, TextSize}; #[derive(Clone, PartialEq, Debug)] pub struct Lit { pub kind: LitKind, - pub symbol: SmolStr, - pub suffix: Option, + pub symbol: Symbol, + pub suffix: Option, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -35,6 +35,9 @@ impl IdentIsRaw { pub fn yes(self) -> bool { matches!(self, IdentIsRaw::Yes) } + pub fn no(&self) -> bool { + matches!(self, IdentIsRaw::No) + } pub fn as_str(self) -> &'static str { match self { IdentIsRaw::No => "", @@ -197,25 +200,30 @@ pub enum DelimiterKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Literal { // escaped - pub text: SmolStr, + pub symbol: Symbol, pub span: S, pub kind: LitKind, - pub suffix: Option>, + pub suffix: Option, } -pub fn token_to_literal(text: SmolStr, span: S) -> Literal +pub fn token_to_literal(text: &str, span: S) -> Literal where S: Copy, { use rustc_lexer::LiteralKind; - let token = rustc_lexer::tokenize(&text).next_tuple(); + let token = rustc_lexer::tokenize(text).next_tuple(); let Some((rustc_lexer::Token { kind: rustc_lexer::TokenKind::Literal { kind, suffix_start }, .. },)) = token else { - return Literal { span, text, kind: LitKind::Err(()), suffix: None }; + return Literal { + span, + symbol: Symbol::intern(text), + kind: LitKind::Err(()), + suffix: None, + }; }; let (kind, start_offset, end_offset) = match kind { @@ -247,10 +255,10 @@ where let lit = &lit[start_offset..lit.len() - end_offset]; let suffix = match suffix { "" | "_" => None, - suffix => Some(Box::new(suffix.into())), + suffix => Some(Symbol::intern(suffix)), }; - Literal { span, text: lit.into(), kind, suffix } + Literal { span, symbol: Symbol::intern(lit), kind, suffix } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -323,22 +331,16 @@ pub enum Spacing { /// Identifier or keyword. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Ident { - pub text: SmolStr, + pub sym: Symbol, pub span: S, pub is_raw: IdentIsRaw, } impl Ident { - pub fn new(text: impl Into + AsRef, span: S) -> Self { - let t = text.as_ref(); + pub fn new(text: &str, span: S) -> Self { // let raw_stripped = IdentIsRaw::split_from_symbol(text.as_ref()); - let raw_stripped = t.strip_prefix("r#"); - let is_raw = if raw_stripped.is_none() { IdentIsRaw::No } else { IdentIsRaw::Yes }; - let text = match raw_stripped { - Some(derawed) => derawed.into(), - None => text.into(), - }; - Ident { text, span, is_raw } + let (is_raw, text) = IdentIsRaw::split_from_symbol(text); + Ident { sym: Symbol::intern(text), span, is_raw } } } @@ -389,8 +391,8 @@ fn print_debug_token( "{}LITERAL {:?} {}{} {:#?}", align, lit.kind, - lit.text, - lit.suffix.as_ref().map(|it| &***it).unwrap_or(""), + lit.symbol, + lit.suffix.as_ref().map(|it| it.as_str()).unwrap_or(""), lit.span )?; } @@ -410,7 +412,7 @@ fn print_debug_token( "{}IDENT {}{} {:#?}", align, ident.is_raw.as_str(), - ident.text, + ident.sym, ident.span )?; } @@ -479,26 +481,26 @@ impl fmt::Display for Leaf { impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.is_raw.as_str(), f)?; - fmt::Display::fmt(&self.text, f) + fmt::Display::fmt(&self.sym, f) } } impl fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { - LitKind::Byte => write!(f, "b'{}'", self.text), - LitKind::Char => write!(f, "'{}'", self.text), - LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", self.text), - LitKind::Str => write!(f, "\"{}\"", self.text), - LitKind::ByteStr => write!(f, "b\"{}\"", self.text), - LitKind::CStr => write!(f, "c\"{}\"", self.text), + LitKind::Byte => write!(f, "b'{}'", self.symbol), + LitKind::Char => write!(f, "'{}'", self.symbol), + LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", self.symbol), + LitKind::Str => write!(f, "\"{}\"", self.symbol), + LitKind::ByteStr => write!(f, "b\"{}\"", self.symbol), + LitKind::CStr => write!(f, "c\"{}\"", self.symbol), LitKind::StrRaw(num_of_hashes) => { let num_of_hashes = num_of_hashes as usize; write!( f, r#"r{0:# { @@ -507,7 +509,7 @@ impl fmt::Display for Literal { f, r#"br{0:# { @@ -516,7 +518,7 @@ impl fmt::Display for Literal { f, r#"cr{0:# Subtree { let s = match child { TokenTree::Leaf(it) => { let s = match it { - Leaf::Literal(it) => it.text.to_string(), + Leaf::Literal(it) => it.symbol.to_string(), Leaf::Punct(it) => it.char.to_string(), - Leaf::Ident(it) => format!("{}{}", it.is_raw.as_str(), it.text), + Leaf::Ident(it) => format!("{}{}", it.is_raw.as_str(), it.sym), }; match (it, last) { (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => { @@ -599,9 +601,9 @@ pub fn pretty(tkns: &[TokenTree]) -> String { fn tokentree_to_text(tkn: &TokenTree) -> String { match tkn { TokenTree::Leaf(Leaf::Ident(ident)) => { - format!("{}{}", ident.is_raw.as_str(), ident.text) + format!("{}{}", ident.is_raw.as_str(), ident.sym) } - TokenTree::Leaf(Leaf::Literal(literal)) => literal.text.clone().into(), + TokenTree::Leaf(Leaf::Literal(literal)) => literal.symbol.as_str().to_owned(), TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char), TokenTree::Subtree(subtree) => { let content = pretty(&subtree.token_trees); From 9ce066e6faa1bfcec7792a0fae4231a5bd7a5a56 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 Jul 2024 10:36:59 +0200 Subject: [PATCH 118/806] Use symbol in cfg --- src/tools/rust-analyzer/Cargo.lock | 4 +- src/tools/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 4 +- .../rust-analyzer/crates/cfg/src/cfg_expr.rs | 39 +++++++++++-------- src/tools/rust-analyzer/crates/cfg/src/dnf.rs | 19 +++++++-- src/tools/rust-analyzer/crates/cfg/src/lib.rs | 16 ++++---- .../rust-analyzer/crates/cfg/src/tests.rs | 26 +++++++------ .../crates/hir-expand/src/builtin_fn_macro.rs | 7 +++- .../crates/hir-expand/src/cfg_process.rs | 25 +++++++----- .../src/completions/attribute/cfg.rs | 2 + src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- .../rust-analyzer/crates/ide/src/runnables.rs | 4 +- .../rust-analyzer/crates/intern/src/symbol.rs | 8 +++- .../crates/intern/src/symbol/symbols.rs | 29 ++++++++------ .../crates/project-model/Cargo.toml | 1 + .../crates/project-model/src/cfg.rs | 7 +++- .../crates/project-model/src/tests.rs | 5 ++- .../crates/project-model/src/workspace.rs | 21 +++++----- .../crates/rust-analyzer/Cargo.toml | 1 + .../crates/rust-analyzer/src/config.rs | 8 +++- .../crates/rust-analyzer/src/target_spec.rs | 3 +- .../crates/test-fixture/src/lib.rs | 4 +- 22 files changed, 147 insertions(+), 92 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 241392edb1e5d..c9542ead79070 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -146,10 +146,10 @@ dependencies = [ "arbitrary", "derive_arbitrary", "expect-test", + "intern", "mbe", "oorandom", "rustc-hash", - "smol_str", "syntax", "tt", ] @@ -1416,6 +1416,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", + "intern", "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", @@ -1656,6 +1657,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", + "intern", "itertools", "load-cargo", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d4c3b7a3bfbd2..428d11ad60d48 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -50,7 +50,7 @@ debug = 2 [workspace.dependencies] # local crates base-db = { path = "./crates/base-db", version = "0.0.0" } -cfg = { path = "./crates/cfg", version = "0.0.0" } +cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } flycheck = { path = "./crates/flycheck", version = "0.0.0" } hir = { path = "./crates/hir", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 784e86649d1ab..faf93f62c6afb 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -15,8 +15,8 @@ doctest = false rustc-hash.workspace = true # locals deps -tt.workspace = true -smol_str.workspace = true +tt = { workspace = true, optional = true } +intern.workspace = true [dev-dependencies] expect-test = "1.4.1" diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index 9c95f0e4e4baa..e4c2a28fb0607 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -2,20 +2,20 @@ //! //! See: -use std::{fmt, slice::Iter as SliceIter}; +use std::fmt; -use smol_str::SmolStr; +use intern::Symbol; /// A simple configuration value passed in from the outside. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CfgAtom { /// eg. `#[cfg(test)]` - Flag(SmolStr), + Flag(Symbol), /// eg. `#[cfg(target_os = "linux")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`. - KeyValue { key: SmolStr, value: SmolStr }, + KeyValue { key: Symbol, value: Symbol }, } impl fmt::Display for CfgAtom { @@ -44,6 +44,7 @@ impl From for CfgExpr { } impl CfgExpr { + #[cfg(feature = "tt")] pub fn parse(tt: &tt::Subtree) -> CfgExpr { next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) } @@ -63,7 +64,11 @@ impl CfgExpr { } } } -fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { + +#[cfg(feature = "tt")] +fn next_cfg_expr(it: &mut std::slice::Iter<'_, tt::TokenTree>) -> Option { + use intern::sym; + let name = match it.next() { None => return None, Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), @@ -77,9 +82,7 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); - // FIXME: escape? - let value = literal.symbol.as_str().into(); - CfgAtom::KeyValue { key: name.as_str().into(), value }.into() + CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into() } _ => return Some(CfgExpr::Invalid), } @@ -88,14 +91,16 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option it.next(); let mut sub_it = subtree.token_trees.iter(); let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); - match name.as_str() { - "all" => CfgExpr::All(subs), - "any" => CfgExpr::Any(subs), - "not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), + match &name { + s if *s == sym::all => CfgExpr::All(subs), + s if *s == sym::any => CfgExpr::Any(subs), + s if *s == sym::not => { + CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))) + } _ => CfgExpr::Invalid, } } - _ => CfgAtom::Flag(name.as_str().into()).into(), + _ => CfgAtom::Flag(name).into(), }; // Eat comma separator @@ -111,11 +116,11 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option impl arbitrary::Arbitrary<'_> for CfgAtom { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { if u.arbitrary()? { - Ok(CfgAtom::Flag(String::arbitrary(u)?.into())) + Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?))) } else { Ok(CfgAtom::KeyValue { - key: String::arbitrary(u)?.into(), - value: String::arbitrary(u)?.into(), + key: Symbol::intern(<_>::arbitrary(u)?), + value: Symbol::intern(<_>::arbitrary(u)?), }) } } diff --git a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs index fd80e1ebe683b..58a250829d4f1 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs @@ -66,9 +66,9 @@ impl DnfExpr { } } - res.enabled.sort_unstable(); + res.enabled.sort_unstable_by(compare); res.enabled.dedup(); - res.disabled.sort_unstable(); + res.disabled.sort_unstable_by(compare); res.disabled.dedup(); Some(res) } @@ -114,14 +114,25 @@ impl DnfExpr { }; // Undo the FxHashMap randomization for consistent output. - diff.enable.sort_unstable(); - diff.disable.sort_unstable(); + diff.enable.sort_unstable_by(compare); + diff.disable.sort_unstable_by(compare); Some(diff) }) } } +fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering { + match (a, b) { + (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()), + (CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less, + (CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater, + (CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => { + key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str())) + } + } +} + impl fmt::Display for DnfExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.conjunctions.len() != 1 { diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 5ef7a104dda59..6d46dfb99949b 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -9,9 +9,10 @@ use std::fmt; use rustc_hash::FxHashSet; +use intern::Symbol; + pub use cfg_expr::{CfgAtom, CfgExpr}; pub use dnf::DnfExpr; -use smol_str::SmolStr; /// Configuration options used for conditional compilation on items with `cfg` attributes. /// We have two kind of options in different namespaces: atomic options like `unix`, and @@ -48,11 +49,11 @@ impl CfgOptions { cfg.fold(&|atom| self.enabled.contains(atom)) } - pub fn insert_atom(&mut self, key: SmolStr) { + pub fn insert_atom(&mut self, key: Symbol) { self.enabled.insert(CfgAtom::Flag(key)); } - pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { + pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) { self.enabled.insert(CfgAtom::KeyValue { key, value }); } @@ -66,19 +67,16 @@ impl CfgOptions { } } - pub fn get_cfg_keys(&self) -> impl Iterator { + pub fn get_cfg_keys(&self) -> impl Iterator { self.enabled.iter().map(|it| match it { CfgAtom::Flag(key) => key, CfgAtom::KeyValue { key, .. } => key, }) } - pub fn get_cfg_values<'a>( - &'a self, - cfg_key: &'a str, - ) -> impl Iterator + 'a { + pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator + 'a { self.enabled.iter().filter_map(move |it| match it { - CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value), + CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value), _ => None, }) } diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index dddaf2cce1823..278e5f61de251 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,5 +1,6 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; +use intern::Symbol; use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode, Edition}; @@ -65,22 +66,25 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { #[test] fn test_cfg_expr_parser() { - assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); - assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); + assert_parse_result("#![cfg(foo)]", CfgAtom::Flag(Symbol::intern("foo")).into()); + assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag(Symbol::intern("foo")).into()); assert_parse_result( "#![cfg(not(foo))]", - CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), + CfgExpr::Not(Box::new(CfgAtom::Flag(Symbol::intern("foo")).into())), ); assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); // Only take the first - assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); + assert_parse_result( + r#"#![cfg(foo, bar = "baz")]"#, + CfgAtom::Flag(Symbol::intern("foo")).into(), + ); assert_parse_result( r#"#![cfg(all(foo, bar = "baz"))]"#, CfgExpr::All(vec![ - CfgAtom::Flag("foo".into()).into(), - CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), + CfgAtom::Flag(Symbol::intern("foo")).into(), + CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(), ]), ); @@ -90,7 +94,7 @@ fn test_cfg_expr_parser() { CfgExpr::Not(Box::new(CfgExpr::Invalid)), CfgExpr::All(vec![]), CfgExpr::Invalid, - CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), + CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(), ]), ); } @@ -167,7 +171,7 @@ fn hints() { check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); - opts.insert_atom("test".into()); + opts.insert_atom(Symbol::intern("test")); check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); @@ -180,7 +184,7 @@ fn hints_impossible() { check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); - opts.insert_atom("test".into()); + opts.insert_atom(Symbol::intern("test")); check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); } @@ -188,8 +192,8 @@ fn hints_impossible() { #[test] fn why_inactive() { let mut opts = CfgOptions::default(); - opts.insert_atom("test".into()); - opts.insert_atom("test2".into()); + opts.insert_atom(Symbol::intern("test")); + opts.insert_atom(Symbol::intern("test2")); check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 4b1a3d697280c..e3290f53432b6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -563,7 +563,12 @@ fn concat_bytes_expand( }; for (i, t) in tt.token_trees.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind, suffix: _ })) => { + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind, + suffix: _, + })) => { record_span(*span); match kind { tt::LitKind::Byte => { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 5f038cfe6871d..9f927184193e9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -3,6 +3,7 @@ use std::iter::Peekable; use base_db::CrateId; use cfg::{CfgAtom, CfgExpr}; +use intern::{sym, Symbol}; use rustc_hash::FxHashSet; use syntax::{ ast::{self, Attr, HasAttrs, Meta, VariantList}, @@ -262,13 +263,13 @@ where let name = match iter.next() { None => return None, Some(NodeOrToken::Token(element)) => match element.kind() { - syntax::T![ident] => element.text().to_owned(), + syntax::T![ident] => Symbol::intern(element.text()), _ => return Some(CfgExpr::Invalid), }, Some(_) => return Some(CfgExpr::Invalid), }; - let result = match name.as_str() { - "all" | "any" | "not" => { + let result = match &name { + s if [&sym::all, &sym::any, &sym::not].contains(&s) => { let mut preds = Vec::new(); let Some(NodeOrToken::Node(tree)) = iter.next() else { return Some(CfgExpr::Invalid); @@ -285,10 +286,12 @@ where preds.push(pred); } } - let group = match name.as_str() { - "all" => CfgExpr::All(preds), - "any" => CfgExpr::Any(preds), - "not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))), + let group = match &name { + s if *s == sym::all => CfgExpr::All(preds), + s if *s == sym::any => CfgExpr::Any(preds), + s if *s == sym::not => { + CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))) + } _ => unreachable!(), }; Some(group) @@ -301,13 +304,15 @@ where if (value_token.kind() == syntax::SyntaxKind::STRING) => { let value = value_token.text(); - let value = value.trim_matches('"').into(); - Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name.into(), value })) + Some(CfgExpr::Atom(CfgAtom::KeyValue { + key: name, + value: Symbol::intern(value.trim_matches('"')), + })) } _ => None, } } - _ => Some(CfgExpr::Atom(CfgAtom::Flag(name.into()))), + _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), }, }; if let Some(NodeOrToken::Token(element)) = iter.peek() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 87a286778e631..6e7d50ede066e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -39,6 +39,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion), "target_endian" => ["little", "big"].into_iter().for_each(add_completion), name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { + let s = s.as_str(); let insert_text = format!(r#""{s}""#); let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); item.insert_text(insert_text); @@ -47,6 +48,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { }), }, None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { + let s = s.as_str(); let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); acc.add(item.build(ctx.db)); }), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index f0b35903f38de..a7073f7062e3e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use std::panic::UnwindSafe; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::ChangeWithProcMacros; +use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, @@ -248,7 +248,7 @@ impl Analysis { // FIXME: cfg options // Default to enable test for single file. let mut cfg_options = CfgOptions::default(); - cfg_options.insert_atom("test".into()); + cfg_options.insert_atom(sym::test.clone()); crate_graph.add_crate_root( file_id, Edition::CURRENT, diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index a68ee4f8671f1..362d3238a9aa3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -3,7 +3,7 @@ use std::fmt; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ - db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, + db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ @@ -403,7 +403,7 @@ pub(crate) fn runnable_impl( } fn has_cfg_test(attrs: AttrsWithOwner) -> bool { - attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test")) + attrs.cfgs().any(|cfg| matches!(&cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if *s == sym::test)) } /// Creates a test mod runnable for outline modules at the top of their definition. diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 9e3f6d842f315..ef76192ba83ed 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -139,11 +139,17 @@ impl TaggedArcPtr { } } -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash)] pub struct Symbol { repr: TaggedArcPtr, } +impl fmt::Debug for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} + const _: () = assert!(std::mem::size_of::() == std::mem::size_of::>()); const _: () = assert!(std::mem::align_of::() == std::mem::align_of::>()); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 61b9d099e06a3..04c70e4fae188 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -93,20 +93,20 @@ define_symbols! { __ra_fixup, add_assign, add, - attributes, align_offset, + align, + all, alloc_layout, alloc, + any, as_str, asm, assert, + attributes, begin_panic, bench, bitand_assign, bitand, - notable_trait, - hidden, - local_inner_macros, bitor_assign, bitor, bitxor_assign, @@ -118,6 +118,7 @@ define_symbols! { branch, Break, c_void, + C, call_mut, call_once, call, @@ -146,8 +147,10 @@ define_symbols! { core, coroutine_state, coroutine, + count, crate_type, CStr, + debug_assertions, Debug, default, Default, @@ -172,6 +175,7 @@ define_symbols! { Eq, Err, exchange_malloc, + exhaustive_patterns, f128, f16, f32, @@ -208,11 +212,13 @@ define_symbols! { global_asm, gt, Hash, + hidden, i128, i16, i32, i64, i8, + ignore, Implied, include_bytes, include_str, @@ -237,14 +243,15 @@ define_symbols! { len, line, llvm_asm, + local_inner_macros, log_syntax, lt, macro_rules, - ignore, - count, manually_drop, maybe_uninit, metadata_type, + min_exhaustive_patterns, + miri, missing, module_path, mul_assign, @@ -271,6 +278,7 @@ define_symbols! { None, not, Not, + notable_trait, Ok, opaque, ops, @@ -280,6 +288,7 @@ define_symbols! { Ord, Output, owned_box, + packed, panic_2015, panic_2021, panic_bounds_check, @@ -328,6 +337,7 @@ define_symbols! { rust_2018, rust_2021, rust_2024, + rust_analyzer, rustc_coherence_is_core, rustc_macro_transparency, semitransparent, @@ -335,6 +345,7 @@ define_symbols! { shl, shr_assign, shr, + simd, sized, slice_len_fn, Some, @@ -367,10 +378,6 @@ define_symbols! { u8, Unknown, unpin, - simd, - C, - align, - packed, unreachable_2015, unreachable_2021, unreachable, @@ -378,7 +385,5 @@ define_symbols! { unsize, usize, v1, - exhaustive_patterns, - min_exhaustive_patterns, va_list } diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 097ee1f75cd75..8b34bd3fad1a1 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -25,6 +25,7 @@ itertools.workspace = true # local deps base-db.workspace = true +intern.workspace = true span.workspace = true cfg.workspace = true paths = { workspace = true, features = ["serde1"] } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs index b409bc1ce7ad1..e921e3de722d1 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs @@ -4,6 +4,7 @@ use std::{fmt, str::FromStr}; use cfg::{CfgDiff, CfgOptions}; +use intern::Symbol; use rustc_hash::FxHashMap; use serde::Serialize; @@ -44,8 +45,10 @@ impl Extend for CfgOptions { fn extend>(&mut self, iter: T) { for cfg_flag in iter { match cfg_flag { - CfgFlag::Atom(it) => self.insert_atom(it.into()), - CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()), + CfgFlag::Atom(it) => self.insert_atom(Symbol::intern(&it)), + CfgFlag::KeyValue { key, value } => { + self.insert_key_value(Symbol::intern(&key), Symbol::intern(&value)) + } } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 2762de5997ae9..8d50d4bdfe2c2 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -4,6 +4,7 @@ use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cargo_metadata::Metadata; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; +use intern::sym; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; @@ -180,7 +181,7 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { #[test] fn cargo_hello_world_project_model_with_wildcard_overrides() { let cfg_overrides = CfgOverrides { - global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(), selective: Default::default(), }; let (crate_graph, _proc_macros) = @@ -199,7 +200,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { global: Default::default(), selective: std::iter::once(( "libc".to_owned(), - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(), )) .collect(), }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 5e27ce298735e..e006b70362e99 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -10,6 +10,7 @@ use base_db::{ LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; +use intern::{sym, Symbol}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; @@ -977,8 +978,8 @@ fn cargo_to_crate_graph( if cargo[pkg].is_local { // Add test cfg for local crates - cfg_options.insert_atom("test".into()); - cfg_options.insert_atom("rust_analyzer".into()); + cfg_options.insert_atom(sym::test.clone()); + cfg_options.insert_atom(sym::rust_analyzer.clone()); } override_cfg.apply(&mut cfg_options, &cargo[pkg].name); @@ -1144,8 +1145,8 @@ fn detached_file_to_crate_graph( sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load); let mut cfg_options = CfgOptions::from_iter(rustc_cfg); - cfg_options.insert_atom("test".into()); - cfg_options.insert_atom("rust_analyzer".into()); + cfg_options.insert_atom(sym::test.clone()); + cfg_options.insert_atom(sym::rust_analyzer.clone()); override_cfg.apply(&mut cfg_options, ""); let cfg_options = Arc::new(cfg_options); @@ -1307,7 +1308,7 @@ fn add_target_crate_root( let cfg_options = { let mut opts = cfg_options; for feature in pkg.active_features.iter() { - opts.insert_key_value("feature".into(), feature.into()); + opts.insert_key_value(sym::feature.clone(), Symbol::intern(feature)); } if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) { opts.extend(cfgs.iter().cloned()); @@ -1381,8 +1382,8 @@ fn sysroot_to_crate_graph( &CfgOverrides { global: CfgDiff::new( vec![ - CfgAtom::Flag("debug_assertions".into()), - CfgAtom::Flag("miri".into()), + CfgAtom::Flag(sym::debug_assertions.clone()), + CfgAtom::Flag(sym::miri.clone()), ], vec![], ) @@ -1394,7 +1395,7 @@ fn sysroot_to_crate_graph( let mut pub_deps = vec![]; let mut libproc_macro = None; - let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(); + let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap(); for (cid, c) in cg.iter_mut() { // uninject `test` flag so `core` keeps working. Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone()); @@ -1449,8 +1450,8 @@ fn sysroot_to_crate_graph( let cfg_options = Arc::new({ let mut cfg_options = CfgOptions::default(); cfg_options.extend(rustc_cfg); - cfg_options.insert_atom("debug_assertions".into()); - cfg_options.insert_atom("miri".into()); + cfg_options.insert_atom(sym::debug_assertions.clone()); + cfg_options.insert_atom(sym::miri.clone()); cfg_options }); let sysroot_crates: FxHashMap = stitched diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 93fb55ede8ea2..bc1b13a649700 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -54,6 +54,7 @@ hir-def.workspace = true hir-ty.workspace = true hir.workspace = true ide-db.workspace = true +intern.workspace = true # This should only be used in CLI ide-ssr.workspace = true ide.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 91cde4dc0adbb..a0ec3920e6e29 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -8,6 +8,7 @@ use std::{fmt, iter, ops::Not, sync::OnceLock}; use cfg::{CfgAtom, CfgDiff}; use dirs::config_dir; use flycheck::{CargoOptions, FlycheckConfig}; +use hir::Symbol; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, @@ -1691,8 +1692,11 @@ impl Config { self.cargo_cfgs() .iter() .map(|(key, val)| match val { - Some(val) => CfgAtom::KeyValue { key: key.into(), value: val.into() }, - None => CfgAtom::Flag(key.into()), + Some(val) => CfgAtom::KeyValue { + key: Symbol::intern(key), + value: Symbol::intern(val), + }, + None => CfgAtom::Flag(Symbol::intern(key)), }) .collect(), vec![], diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 863ff06439961..045b9e4198a64 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -3,6 +3,7 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; +use hir::sym; use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId}; use project_model::project_json::Runnable; use project_model::{CargoFeatures, ManifestPath, TargetKind}; @@ -237,7 +238,7 @@ impl CargoTargetSpec { /// Fill minimal features needed fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) { match cfg_expr { - CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if key == "feature" => { + CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if *key == sym::feature => { features.push(value.to_string()) } CfgExpr::All(preds) => { diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 7a0c474b750b6..e910094c77232 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -481,9 +481,9 @@ impl FileMeta { let mut cfg = CfgOptions::default(); for (k, v) in f.cfgs { if let Some(v) = v { - cfg.insert_key_value(k.into(), v.into()); + cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v)); } else { - cfg.insert_atom(k.into()); + cfg.insert_atom(Symbol::intern(&k)); } } From 1a20a0803f82446491be0fddfc142a90126bc15f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 Jul 2024 12:05:16 +0200 Subject: [PATCH 119/806] More symbol usage --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/base-db/Cargo.toml | 1 + .../rust-analyzer/crates/base-db/src/input.rs | 34 +++--- .../rust-analyzer/crates/hir-def/src/attr.rs | 100 +++++++++--------- .../crates/hir-def/src/body/lower.rs | 45 ++++---- .../crates/hir-def/src/body/scope.rs | 2 +- .../rust-analyzer/crates/hir-def/src/data.rs | 24 ++--- .../crates/hir-def/src/data/adt.rs | 12 +-- .../rust-analyzer/crates/hir-def/src/hir.rs | 31 +++--- .../crates/hir-def/src/hir/format_args.rs | 11 +- .../crates/hir-def/src/hir/type_ref.rs | 14 +-- .../crates/hir-def/src/item_tree.rs | 6 +- .../crates/hir-def/src/item_tree/lower.rs | 8 +- .../crates/hir-def/src/lang_item.rs | 5 +- .../crates/hir-def/src/nameres/collector.rs | 32 +++--- .../crates/hir-def/src/nameres/proc_macro.rs | 4 +- .../crates/hir-def/src/pretty.rs | 2 +- .../crates/hir-expand/src/attrs.rs | 10 +- .../hir-ty/src/diagnostics/decl_check.rs | 5 +- .../diagnostics/match_check/pat_analysis.rs | 2 +- .../crates/hir-ty/src/display.rs | 4 +- .../crates/hir-ty/src/inhabitedness.rs | 3 +- .../crates/hir-ty/src/layout/adt.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 78 +++++++------- .../rust-analyzer/crates/hir-ty/src/lower.rs | 4 +- .../crates/hir-ty/src/method_resolution.rs | 3 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/eval/shim.rs | 22 ++-- .../crates/hir-ty/src/mir/lower.rs | 1 + .../rust-analyzer/crates/hir-ty/src/utils.rs | 7 +- .../rust-analyzer/crates/hir/src/display.rs | 3 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +-- .../rust-analyzer/crates/hir/src/symbols.rs | 4 +- .../src/handlers/add_missing_match_arms.rs | 4 +- .../handlers/destructure_struct_binding.rs | 4 +- .../src/handlers/move_module_to_file.rs | 4 +- .../crates/ide-completion/src/context.rs | 6 +- .../crates/ide-completion/src/render.rs | 4 +- .../ide-completion/src/render/variant.rs | 4 +- .../crates/ide-db/src/documentation.rs | 8 +- .../rust-analyzer/crates/ide-db/src/search.rs | 6 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 15 +-- .../crates/ide/src/fetch_crates.rs | 2 +- .../rust-analyzer/crates/ide/src/moniker.rs | 2 +- .../ide/src/syntax_highlighting/inject.rs | 6 +- .../crates/intern/src/symbol/symbols.rs | 91 ++++++++++++++-- .../crates/project-model/src/project_json.rs | 1 + .../crates/project-model/src/workspace.rs | 24 ++--- .../rust-analyzer/src/cli/analysis_stats.rs | 4 +- .../crates/test-fixture/src/lib.rs | 6 +- 50 files changed, 388 insertions(+), 303 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index c9542ead79070..4ed4a45014191 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -70,6 +70,7 @@ name = "base-db" version = "0.0.0" dependencies = [ "cfg", + "intern", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lz4_flex", "rustc-hash", diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 4ab99fc33c466..1b1ee034cac2f 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -27,6 +27,7 @@ stdx.workspace = true syntax.workspace = true vfs.workspace = true span.workspace = true +intern.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 1d172ab9e40e3..41b7e271b0c4b 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -9,10 +9,10 @@ use std::{fmt, mem, ops}; use cfg::CfgOptions; +use intern::Symbol; use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; use span::Edition; -use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; @@ -99,8 +99,8 @@ impl fmt::Debug for CrateGraph { pub type CrateId = Idx; -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct CrateName(SmolStr); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CrateName(Symbol); impl CrateName { /// Creates a crate name, checking for dashes in the string provided. @@ -110,16 +110,16 @@ impl CrateName { if name.contains('-') { Err(name) } else { - Ok(Self(SmolStr::new(name))) + Ok(Self(Symbol::intern(name))) } } /// Creates a crate name, unconditionally replacing the dashes with underscores. pub fn normalize_dashes(name: &str) -> CrateName { - Self(SmolStr::new(name.replace('-', "_"))) + Self(Symbol::intern(&name.replace('-', "_"))) } - pub fn as_smol_str(&self) -> &SmolStr { + pub fn symbol(&self) -> &Symbol { &self.0 } } @@ -133,7 +133,7 @@ impl fmt::Display for CrateName { impl ops::Deref for CrateName { type Target = str; fn deref(&self) -> &str { - &self.0 + self.0.as_str() } } @@ -141,11 +141,11 @@ impl ops::Deref for CrateName { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CrateOrigin { /// Crates that are from the rustc workspace. - Rustc { name: String }, + Rustc { name: Symbol }, /// Crates that are workspace members. - Local { repo: Option, name: Option }, + Local { repo: Option, name: Option }, /// Crates that are non member libraries. - Library { repo: Option, name: String }, + Library { repo: Option, name: Symbol }, /// Crates that are provided by the language, like std, core, proc-macro, ... Lang(LangCrateOrigin), } @@ -201,16 +201,16 @@ impl fmt::Display for LangCrateOrigin { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateDisplayName { // The name we use to display various paths (with `_`). crate_name: CrateName, // The name as specified in Cargo.toml (with `-`). - canonical_name: String, + canonical_name: Symbol, } impl CrateDisplayName { - pub fn canonical_name(&self) -> &str { + pub fn canonical_name(&self) -> &Symbol { &self.canonical_name } pub fn crate_name(&self) -> &CrateName { @@ -220,7 +220,7 @@ impl CrateDisplayName { impl From for CrateDisplayName { fn from(crate_name: CrateName) -> CrateDisplayName { - let canonical_name = crate_name.to_string(); + let canonical_name = crate_name.0.clone(); CrateDisplayName { crate_name, canonical_name } } } @@ -239,9 +239,9 @@ impl ops::Deref for CrateDisplayName { } impl CrateDisplayName { - pub fn from_canonical_name(canonical_name: String) -> CrateDisplayName { - let crate_name = CrateName::normalize_dashes(&canonical_name); - CrateDisplayName { crate_name, canonical_name } + pub fn from_canonical_name(canonical_name: &str) -> CrateDisplayName { + let crate_name = CrateName::normalize_dashes(canonical_name); + CrateDisplayName { crate_name, canonical_name: Symbol::intern(canonical_name) } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 1a5ac96aa29bb..723d675347551 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -1,6 +1,6 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. -use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter}; +use std::{borrow::Cow, hash::Hash, ops, slice}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -14,7 +14,7 @@ use la_arena::{ArenaMap, Idx, RawIdx}; use mbe::DelimiterKind; use syntax::{ ast::{self, HasAttrs}, - AstPtr, SmolStr, + AstPtr, }; use triomphe::Arc; @@ -121,12 +121,12 @@ impl Attrs { } impl Attrs { - pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { + pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'_> { AttrQuery { attrs: self, key } } pub fn cfg(&self) -> Option { - let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse); + let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse); let first = cfgs.next()?; match cfgs.next() { Some(second) => { @@ -138,7 +138,7 @@ impl Attrs { } pub fn cfgs(&self) -> impl Iterator + '_ { - self.by_key("cfg").tt_values().map(CfgExpr::parse) + self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse) } pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { @@ -148,50 +148,50 @@ impl Attrs { } } - pub fn lang(&self) -> Option<&str> { - self.by_key("lang").string_value() + pub fn lang(&self) -> Option<&Symbol> { + self.by_key(&sym::lang).string_value() } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(|it| LangItem::from_symbol(&Symbol::intern(it))) + self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol) } pub fn has_doc_hidden(&self) -> bool { - self.by_key("doc").tt_values().any(|tt| { + self.by_key(&sym::doc).tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) }) } pub fn has_doc_notable_trait(&self) -> bool { - self.by_key("doc").tt_values().any(|tt| { + self.by_key(&sym::doc).tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) }) } pub fn doc_exprs(&self) -> impl Iterator + '_ { - self.by_key("doc").tt_values().map(DocExpr::parse) + self.by_key(&sym::doc).tt_values().map(DocExpr::parse) } - pub fn doc_aliases(&self) -> impl Iterator + '_ { + pub fn doc_aliases(&self) -> impl Iterator + '_ { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } - pub fn export_name(&self) -> Option<&str> { - self.by_key("export_name").string_value() + pub fn export_name(&self) -> Option<&Symbol> { + self.by_key(&sym::export_name).string_value() } pub fn is_proc_macro(&self) -> bool { - self.by_key("proc_macro").exists() + self.by_key(&sym::proc_macro).exists() } pub fn is_proc_macro_attribute(&self) -> bool { - self.by_key("proc_macro_attribute").exists() + self.by_key(&sym::proc_macro_attribute).exists() } pub fn is_proc_macro_derive(&self) -> bool { - self.by_key("proc_macro_derive").exists() + self.by_key(&sym::proc_macro_derive).exists() } pub fn is_test(&self) -> bool { @@ -210,27 +210,27 @@ impl Attrs { } pub fn is_ignore(&self) -> bool { - self.by_key("ignore").exists() + self.by_key(&sym::ignore).exists() } pub fn is_bench(&self) -> bool { - self.by_key("bench").exists() + self.by_key(&sym::bench).exists() } pub fn is_unstable(&self) -> bool { - self.by_key("unstable").exists() + self.by_key(&sym::unstable).exists() } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum DocAtom { /// eg. `#[doc(hidden)]` - Flag(SmolStr), + Flag(Symbol), /// eg. `#[doc(alias = "it")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. - KeyValue { key: SmolStr, value: SmolStr }, + KeyValue { key: Symbol, value: Symbol }, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -239,7 +239,7 @@ pub enum DocExpr { /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]` Atom(DocAtom), /// eg. `#[doc(alias("x", "y"))]` - Alias(Vec), + Alias(Vec), } impl From for DocExpr { @@ -253,9 +253,9 @@ impl DocExpr { next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid) } - pub fn aliases(&self) -> &[SmolStr] { + pub fn aliases(&self) -> &[Symbol] { match self { - DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => { + DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => { std::slice::from_ref(value) } DocExpr::Alias(aliases) => aliases, @@ -264,7 +264,7 @@ impl DocExpr { } } -fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { +fn next_doc_expr(it: &mut slice::Iter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), @@ -282,9 +282,7 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option }))) => { it.next(); it.next(); - // FIXME: escape? raw string? - let value = SmolStr::new(text.as_str()); - DocAtom::KeyValue { key: name.as_str().into(), value }.into() + DocAtom::KeyValue { key: name, value: text.clone() }.into() } _ => return Some(DocExpr::Invalid), } @@ -292,12 +290,12 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Subtree(subtree)) => { it.next(); let subs = parse_comma_sep(subtree); - match name.as_str() { - "alias" => DocExpr::Alias(subs), + match &name { + s if *s == sym::alias => DocExpr::Alias(subs), _ => DocExpr::Invalid, } } - _ => DocAtom::Flag(name.as_str().into()).into(), + _ => DocAtom::Flag(name).into(), }; // Eat comma separator @@ -309,16 +307,16 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(ret) } -fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { +fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { subtree .token_trees .iter() .filter_map(|tt| match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, - symbol: text, + symbol, .. - })) => Some(SmolStr::new(text.as_str())), + })) => Some(symbol.clone()), _ => None, }) .collect() @@ -565,7 +563,7 @@ impl AttrSourceMap { #[derive(Debug, Clone, Copy)] pub struct AttrQuery<'attr> { attrs: &'attr Attrs, - key: &'static str, + key: &'attr Symbol, } impl<'attr> AttrQuery<'attr> { @@ -573,11 +571,11 @@ impl<'attr> AttrQuery<'attr> { self.attrs().filter_map(|attr| attr.token_tree_value()) } - pub fn string_value(self) -> Option<&'attr str> { + pub fn string_value(self) -> Option<&'attr Symbol> { self.attrs().find_map(|attr| attr.string_value()) } - pub fn string_value_with_span(self) -> Option<(&'attr str, span::Span)> { + pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> { self.attrs().find_map(|attr| attr.string_value_with_span()) } @@ -591,9 +589,7 @@ impl<'attr> AttrQuery<'attr> { pub fn attrs(self) -> impl Iterator + Clone { let key = self.key; - self.attrs - .iter() - .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_smol_str() == key)) + self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key)) } /// Find string value for a specific key inside token tree @@ -602,10 +598,10 @@ impl<'attr> AttrQuery<'attr> { /// #[doc(html_root_url = "url")] /// ^^^^^^^^^^^^^ key /// ``` - pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&str> { + pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&str> { self.tt_values().find_map(|tt| { let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if sym.as_str() == key)) + .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) .nth(2); match name { @@ -660,6 +656,7 @@ mod tests { //! This module contains tests for doc-expression parsing. //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`. + use intern::Symbol; use triomphe::Arc; use base_db::FileId; @@ -685,24 +682,29 @@ mod tests { #[test] fn test_doc_expr_parser() { - assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into()); + assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into()); assert_parse_result( r#"#![doc(alias = "foo")]"#, - DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(), + DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(), ); - assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into())); + assert_parse_result( + r#"#![doc(alias("foo"))]"#, + DocExpr::Alias([Symbol::intern("foo")].into()), + ); assert_parse_result( r#"#![doc(alias("foo", "bar", "baz"))]"#, - DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()), + DocExpr::Alias( + [Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(), + ), ); assert_parse_result( r#" #[doc(alias("Bar", "Qux"))] struct Foo;"#, - DocExpr::Alias(["Bar".into(), "Qux".into()].into()), + DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()), ); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index b96745022a21a..b6d43af2eb04c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -8,7 +8,7 @@ use hir_expand::{ name::{AsName, Name}, ExpandError, InFile, }; -use intern::{sym, Interned}; +use intern::{sym, Interned, Symbol}; use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::AstIdMap; @@ -1623,30 +1623,29 @@ impl ExprCollector<'_> { } } - let lit_pieces = - fmt.template - .iter() - .enumerate() - .filter_map(|(i, piece)| { - match piece { - FormatArgsPiece::Literal(s) => Some( - self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))), - ), - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 - || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) - { - Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( - "".into(), - )))) - } else { - None - } + let lit_pieces = fmt + .template + .iter() + .enumerate() + .filter_map(|(i, piece)| { + match piece { + FormatArgsPiece::Literal(s) => { + Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone())))) + } + &FormatArgsPiece::Placeholder(_) => { + // Inject empty string before placeholders when not already preceded by a literal piece. + if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) + { + Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( + Symbol::empty(), + )))) + } else { + None } } - }) - .collect(); + } + }) + .collect(); let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces, is_assignee_expr: false, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index fd685235e17db..3fc244a1e873b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -338,7 +338,7 @@ mod tests { let actual = scopes .scope_chain(scope) .flat_map(|scope| scopes.entries(scope)) - .map(|it| it.name().to_smol_str()) + .map(|it| it.name().as_str()) .collect::>() .join("\n"); let expected = expected.join("\n"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index e2bb02c0c13ff..a887612707470 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -6,7 +6,7 @@ use base_db::CrateId; use hir_expand::{ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind, }; -use intern::{sym, Interned}; +use intern::{sym, Interned, Symbol}; use smallvec::SmallVec; use syntax::{ast, Parse}; use triomphe::Arc; @@ -38,7 +38,7 @@ pub struct FunctionData { pub ret_type: Interned, pub attrs: Attrs, pub visibility: RawVisibility, - pub abi: Option>, + pub abi: Option, pub legacy_const_generics_indices: Box<[u32]>, pub rustc_allow_incoherent_impl: bool, flags: FnFlags, @@ -92,12 +92,12 @@ impl FunctionData { let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let legacy_const_generics_indices = attrs - .by_key("rustc_legacy_const_generics") + .by_key(&sym::rustc_legacy_const_generics) .tt_values() .next() .map(parse_rustc_legacy_const_generics) .unwrap_or_default(); - let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); + let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); Arc::new(FunctionData { name: func.name.clone(), @@ -200,8 +200,8 @@ impl TypeAliasData { ModItem::from(loc.id.value).into(), ); let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); + attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); + let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); Arc::new(TypeAliasData { name: typ.name.clone(), @@ -251,10 +251,10 @@ impl TraitData { let visibility = item_tree[tr_def.visibility].clone(); let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()); let skip_array_during_method_dispatch = - attrs.by_key("rustc_skip_array_during_method_dispatch").exists(); + attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists(); let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); + let fundamental = attrs.by_key(&sym::fundamental).exists(); let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); @@ -393,7 +393,7 @@ impl Macro2Data { let helpers = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .by_key("rustc_builtin_macro") + .by_key(&sym::rustc_builtin_macro) .tt_values() .next() .and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees)) @@ -423,7 +423,7 @@ impl MacroRulesData { let macro_export = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .by_key("macro_export") + .by_key(&sym::macro_export) .exists(); Arc::new(MacroRulesData { name: makro.name.clone(), macro_export }) @@ -526,7 +526,7 @@ impl ConstData { let rustc_allow_incoherent_impl = item_tree .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into()) - .by_key("rustc_allow_incoherent_impl") + .by_key(&sym::rustc_allow_incoherent_impl) .exists(); Arc::new(ConstData { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 3942c2a98af78..cc6e408b65bac 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -95,7 +95,7 @@ fn repr_from_value( item_tree: &ItemTree, of: AttrOwner, ) -> Option { - item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt) + item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt) } fn parse_repr_tt(tt: &Subtree) -> Option { @@ -194,10 +194,10 @@ impl StructData { let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; - if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; } - if attrs.by_key("fundamental").exists() { + if attrs.by_key(&sym::fundamental).exists() { flags |= StructFlags::IS_FUNDAMENTAL; } if let Some(lang) = attrs.lang_item() { @@ -248,10 +248,10 @@ impl StructData { let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; - if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; } - if attrs.by_key("fundamental").exists() { + if attrs.by_key(&sym::fundamental).exists() { flags |= StructFlags::IS_FUNDAMENTAL; } @@ -287,7 +287,7 @@ impl EnumData { let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let rustc_has_incoherent_inherent_impls = item_tree .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) - .by_key("rustc_has_incoherent_inherent_impls") + .by_key(&sym::rustc_has_incoherent_inherent_impls) .exists(); let enum_ = &item_tree[loc.id.value]; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index d306f9be657a1..d7eb80a88bfa0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -18,7 +18,7 @@ pub mod type_ref; use std::fmt; use hir_expand::name::Name; -use intern::Interned; +use intern::{Interned, Symbol}; use la_arena::{Idx, RawIdx}; use rustc_apfloat::ieee::{Half as f16, Quad as f128}; use smallvec::SmallVec; @@ -60,41 +60,41 @@ pub type LabelId = Idx