From 90722811c8818b1ce4c7819dac41f22837fa98c6 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 1 Apr 2022 18:51:57 -0700 Subject: [PATCH 0001/1080] Deprecate the `ptr_to_from_bits` feature The strict provenance APIs are a better version of these, and things like `.addr()` work for the "that cast looks sketchy" case even if the full strict provenance stuff never happens. So I think it's fine to move away from these, and encourage the others instead. --- library/core/src/ptr/const_ptr.rs | 5 +++++ library/core/src/ptr/mut_ptr.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index af64fbc8e9eca..e460a7f3b1e3e 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -119,6 +119,7 @@ impl *const T { /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] + #[rustc_deprecated(since = "1.62", reason = "replaced by the `addr` method")] pub fn to_bits(self) -> usize where T: Sized, @@ -140,6 +141,10 @@ impl *const T { /// assert_eq!(<*const u8>::from_bits(1), dangling); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] + #[rustc_deprecated( + since = "1.62", + reason = "replaced by the `with_addr` method or the `ptr::invalid` function" + )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function pub fn from_bits(bits: usize) -> Self where diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index d6872ba1c20f0..351012e6ffc8f 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -125,6 +125,7 @@ impl *mut T { /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] + #[rustc_deprecated(since = "1.62", reason = "replaced by the `addr` method")] pub fn to_bits(self) -> usize where T: Sized, @@ -146,6 +147,10 @@ impl *mut T { /// assert_eq!(<*mut u8>::from_bits(1), dangling); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] + #[rustc_deprecated( + since = "1.62", + reason = "replaced by the `with_addr` method or the `ptr::invalid_mut` function" + )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function pub fn from_bits(bits: usize) -> Self where From 4d37d1f4226bd92bf6c129977357afd50993cf0a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 3 Apr 2022 13:19:30 -0700 Subject: [PATCH 0002/1080] Refer to the `exposed` versions of the methods instead Changing to those doesn't introduce any new unsoundness over the existing ones, so they're the better "if you won't want to think about it" replacement. But also mention the strict provenance APIs, as that's what we'd rather they use instead. --- library/core/src/ptr/const_ptr.rs | 9 +++++++-- library/core/src/ptr/mut_ptr.rs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index e460a7f3b1e3e..2c371f635ab97 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -119,7 +119,11 @@ impl *const T { /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[rustc_deprecated(since = "1.62", reason = "replaced by the `addr` method")] + #[rustc_deprecated( + since = "1.62", + reason = "replaced by the `exposed_addr` method, or update your code \ + to follow the strict provenance rules using its APIs" + )] pub fn to_bits(self) -> usize where T: Sized, @@ -143,7 +147,8 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[rustc_deprecated( since = "1.62", - reason = "replaced by the `with_addr` method or the `ptr::invalid` function" + reason = "replaced by the `ptr::from_exposed_addr` function, or update \ + your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function pub fn from_bits(bits: usize) -> Self diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 351012e6ffc8f..7968253156237 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -125,7 +125,11 @@ impl *mut T { /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); /// ``` #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[rustc_deprecated(since = "1.62", reason = "replaced by the `addr` method")] + #[rustc_deprecated( + since = "1.62", + reason = "replaced by the `exposed_addr` method, or update your code \ + to follow the strict provenance rules using its APIs" + )] pub fn to_bits(self) -> usize where T: Sized, @@ -149,7 +153,8 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[rustc_deprecated( since = "1.62", - reason = "replaced by the `with_addr` method or the `ptr::invalid_mut` function" + reason = "replaced by the `ptr::from_exposed_addr_mut` function, or \ + update your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function pub fn from_bits(bits: usize) -> Self From 9208c08a771199e49aa1bff0e80354124eddbe5f Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Wed, 8 Jun 2022 12:08:00 +0100 Subject: [PATCH 0003/1080] Use liballoc's specialised in-place vec collection liballoc already specialises in-place vector collection, so manually reimplementing it in `IdFunctor::try_map_id` was superfluous. --- compiler/rustc_data_structures/src/functor.rs | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_data_structures/src/functor.rs b/compiler/rustc_data_structures/src/functor.rs index a3d3f988344c6..84cb417dd8936 100644 --- a/compiler/rustc_data_structures/src/functor.rs +++ b/compiler/rustc_data_structures/src/functor.rs @@ -34,43 +34,11 @@ impl IdFunctor for Vec { type Inner = T; #[inline] - fn try_map_id(self, mut f: F) -> Result + fn try_map_id(self, f: F) -> Result where F: FnMut(Self::Inner) -> Result, { - struct HoleVec { - vec: Vec>, - hole: Option, - } - - impl Drop for HoleVec { - fn drop(&mut self) { - unsafe { - for (index, slot) in self.vec.iter_mut().enumerate() { - if self.hole != Some(index) { - mem::ManuallyDrop::drop(slot); - } - } - } - } - } - - unsafe { - let (ptr, length, capacity) = self.into_raw_parts(); - let vec = Vec::from_raw_parts(ptr.cast(), length, capacity); - let mut hole_vec = HoleVec { vec, hole: None }; - - for (index, slot) in hole_vec.vec.iter_mut().enumerate() { - hole_vec.hole = Some(index); - let original = mem::ManuallyDrop::take(slot); - let mapped = f(original)?; - *slot = mem::ManuallyDrop::new(mapped); - hole_vec.hole = None; - } - - mem::forget(hole_vec); - Ok(Vec::from_raw_parts(ptr, length, capacity)) - } + self.into_iter().map(f).collect() } } From a6444a69e27275c69e7287fe02ba0c88d554c445 Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:05:57 +0200 Subject: [PATCH 0004/1080] Remove if_chain from equatable_if_let --- clippy_lints/src/equatable_if_let.rs | 59 +++++++++++++--------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index fdfb821ac7895..ba615c8c16484 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,37 +66,33 @@ 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_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); - let exp_ty = cx.typeck_results().expr_ty(let_expr.init); - let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { - - let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); - } + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) + && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) + && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) + && is_structural_partial_eq(cx, exp_ty, pat_ty) { + let mut applicability = Applicability::MachineApplicable; + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); } } } From 5ee1c24f28029162ad25614c092224cc94ba59ff Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:46:04 +0200 Subject: [PATCH 0005/1080] Lint suggests matches macro if PartialEq trait is not implemented --- clippy_lints/src/equatable_if_let.rs | 64 +++++++++++++++++----------- tests/ui/equatable_if_let.rs | 7 +++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index ba615c8c16484..6f26195d105ce 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -68,31 +68,47 @@ 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 - && unary_pattern(let_expr.pat) - && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) - && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) - && is_structural_partial_eq(cx, exp_ty, pat_ty) { + && unary_pattern(let_expr.pat) { + let exp_ty = cx.typeck_results().expr_ty(let_expr.init); + let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); + + if is_structural_partial_eq(cx, exp_ty, pat_ty) { + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ) + } } } } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 8c467d14d2a9f..c3626c081dd5e 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -70,6 +76,7 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + if let NoPartialEqStruct { a: 2, b: false } = h {} macro_rules! m1 { (x) => { From 4eaadd622d21948c488a4952b3c65fd5b9d852dc Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:50:34 +0200 Subject: [PATCH 0006/1080] Run cargo dev bless to update fixes & stderr --- clippy_lints/src/equatable_if_let.rs | 10 +++---- tests/ui/equatable_if_let.fixed | 11 ++++++-- tests/ui/equatable_if_let.stderr | 42 ++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 6f26195d105ce..928128785f49a 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -96,18 +96,18 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ); } else { span_lint_and_sugg( - cx, + cx, EQUATABLE_IF_LET, expr.span, - "this pattern matching can be expressed using `matches!`", - "try", + "this pattern matching can be expressed using `matches!`", + "try", format!( "matches!({}, {})", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), + ), applicability, - ) + ); } } } diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 687efdada6e31..9af2ba9627200 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -66,10 +72,11 @@ fn main() { if let Some(3 | 4) = c {} if let Struct { a, b: false } = d {} if let Struct { a: 2, b: x } = d {} - if let NotPartialEq::A = f {} + if matches!(f, NotPartialEq::A) {} if g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} + if matches!(Some(f), Some(NotPartialEq::A)) {} if Some(g) == Some(NotStructuralEq::A) {} + if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 9c4c3cc3682e6..40ca75b8da22c 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:59:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,64 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:57:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:58:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let NotPartialEq::A = f {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:70:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:77:8 + | +LL | if let Some(NotPartialEq::A) = Some(f) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:72:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: this pattern matching can be expressed using equality +error: this pattern matching can be expressed using `matches!` --> $DIR/equatable_if_let.rs:79:8 | +LL | if let NoPartialEqStruct { a: 2, b: false } = h {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:86:8 + | LL | if let m1!(x) = "abc" { | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors From f613bd6d3af8a28f9ea8a499b4524decaf67f198 Mon Sep 17 00:00:00 2001 From: Stefan Schindler Date: Sat, 10 Sep 2022 16:24:05 +0200 Subject: [PATCH 0007/1080] Make the one-liner more descriptive --- library/alloc/src/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 65e323c9e00c3..00c922f3ae93f 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1,4 +1,4 @@ -//! The `Box` type for heap allocation. +//! The pointer type for heap allocation for an owned value of type `T`. //! //! [`Box`], casually referred to as a 'box', provides the simplest form of //! heap allocation in Rust. Boxes provide ownership for this allocation, and @@ -187,7 +187,7 @@ pub use thin::ThinBox; mod thin; -/// A pointer type for heap allocation. +/// A pointer type for heap allocation for an owned value of type `T`. /// /// See the [module-level documentation](../../std/boxed/index.html) for more. #[lang = "owned_box"] From c7961da935952380b150ff3ce8c6ca2323a182ec Mon Sep 17 00:00:00 2001 From: zhaixiaojuan Date: Sat, 17 Sep 2022 18:00:34 +0800 Subject: [PATCH 0008/1080] Add loongarch64 abi support --- .../rustc_target/src/abi/call/loongarch.rs | 342 ++++++++++++++++++ compiler/rustc_target/src/abi/call/mod.rs | 2 + 2 files changed, 344 insertions(+) create mode 100644 compiler/rustc_target/src/abi/call/loongarch.rs diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs new file mode 100644 index 0000000000000..d29b479de5da6 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -0,0 +1,342 @@ +use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use crate::spec::HasTargetSpec; + +#[derive(Copy, Clone)] +enum RegPassKind { + Float(Reg), + Integer(Reg), + Unknown, +} + +#[derive(Copy, Clone)] +enum FloatConv { + FloatPair(Reg, Reg), + Float(Reg), + MixedPair(Reg, Reg), +} + +#[derive(Copy, Clone)] +struct CannotUseFpConv; + +fn is_loongarch_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { + match arg.layout.abi { + Abi::Vector { .. } => true, + _ => arg.layout.is_aggregate(), + } +} + +fn should_use_fp_conv_helper<'a, Ty, C>( + cx: &C, + arg_layout: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, + field1_kind: &mut RegPassKind, + field2_kind: &mut RegPassKind, +) -> Result<(), CannotUseFpConv> +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + match arg_layout.abi { + Abi::Scalar(scalar) => match scalar.primitive() { + abi::Int(..) | abi::Pointer => { + if arg_layout.size.bits() > xlen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + (RegPassKind::Float(_), RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + _ => return Err(CannotUseFpConv), + } + } + abi::F32 | abi::F64 => { + if arg_layout.size.bits() > flen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + (_, RegPassKind::Unknown) => { + *field2_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + _ => return Err(CannotUseFpConv), + } + } + }, + Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), + Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + return Err(CannotUseFpConv); + } + } + FieldsShape::Array { count, .. } => { + for _ in 0..count { + let elem_layout = arg_layout.field(cx, 0); + should_use_fp_conv_helper( + cx, + &elem_layout, + xlen, + flen, + field1_kind, + field2_kind, + )?; + } + } + FieldsShape::Arbitrary { .. } => { + match arg_layout.variants { + abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), + abi::Variants::Single { .. } => (), + } + for i in arg_layout.fields.index_by_increasing_offset() { + let field = arg_layout.field(cx, i); + should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + } + } + }, + } + Ok(()) +} + +fn should_use_fp_conv<'a, Ty, C>( + cx: &C, + arg: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, +) -> Option +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + let mut field1_kind = RegPassKind::Unknown; + let mut field2_kind = RegPassKind::Unknown; + if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + return None; + } + match (field1_kind, field2_kind) { + (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), + (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + _ => None, + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { + match conv { + FloatConv::Float(f) => { + arg.cast_to(f); + } + FloatConv::FloatPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + FloatConv::MixedPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + } + return false; + } + + let total = arg.layout.size; + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + return true; + } + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + if is_loongarch_aggregate(arg) { + if total.bits() <= xlen { + arg.cast_to(xlen_reg); + } else { + arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + } + return false; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + extend_integer_width(arg, xlen); + false +} + +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + xlen: u64, + flen: u64, + is_vararg: bool, + avail_gprs: &mut u64, + avail_fprs: &mut u64, +) where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if !is_vararg { + match should_use_fp_conv(cx, &arg.layout, xlen, flen) { + Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { + *avail_fprs -= 1; + arg.cast_to(f); + return; + } + Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + *avail_fprs -= 2; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + *avail_gprs -= 1; + *avail_fprs -= 1; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + _ => (), + } + } + + let total = arg.layout.size; + let align = arg.layout.align.abi.bits(); + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + let double_xlen_reg = match xlen { + 32 => Reg::i64(), + 64 => Reg::i128(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + if total.bits() > xlen { + let align_regs = align > xlen; + if is_loongarch_aggregate(arg) { + arg.cast_to(Uniform { + unit: if align_regs { double_xlen_reg } else { xlen_reg }, + total: Size::from_bits(xlen * 2), + }); + } + if align_regs && is_vararg { + *avail_gprs -= *avail_gprs % 2; + } + if *avail_gprs >= 2 { + *avail_gprs -= 2; + } else { + *avail_gprs = 0; + } + return; + } else if is_loongarch_aggregate(arg) { + arg.cast_to(xlen_reg); + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + if *avail_gprs >= 1 { + extend_integer_width(arg, xlen); + *avail_gprs -= 1; + } +} + +fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { + if let Abi::Scalar(scalar) = arg.layout.abi { + if let abi::Int(i, _) = scalar.primitive() { + // 32-bit integers are always sign-extended + if i.size().bits() == 32 && xlen > 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.ext(ArgExtension::Sext); + return; + } + } + } + } + + arg.extend_integer_width_to(xlen); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + let xlen = cx.data_layout().pointer_size.bits(); + let flen = match &cx.target_spec().llvm_abiname[..] { + "ilp32f" | "lp64f" => 32, + "ilp32d" | "lp64d" => 64, + _ => 0, + }; + + let mut avail_gprs = 8; + let mut avail_fprs = 8; + + if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) { + avail_gprs -= 1; + } + + for (i, arg) in fn_abi.args.iter_mut().enumerate() { + if arg.is_ignore() { + continue; + } + classify_arg( + cx, + arg, + xlen, + flen, + i >= fn_abi.fixed_count as usize, + &mut avail_gprs, + &mut avail_fprs, + ); + } +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index d2fb8c32ffd27..bd3cb7951edb0 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -10,6 +10,7 @@ mod arm; mod avr; mod bpf; mod hexagon; +mod loongarch; mod m68k; mod mips; mod mips64; @@ -696,6 +697,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "amdgpu" => amdgpu::compute_abi_info(cx, self), "arm" => arm::compute_abi_info(cx, self), "avr" => avr::compute_abi_info(self), + "loongarch64" => loongarch::compute_abi_info(cx, self), "m68k" => m68k::compute_abi_info(self), "mips" => mips::compute_abi_info(cx, self), "mips64" => mips64::compute_abi_info(cx, self), From fc380ecd13f0a16519ebf1df64648d006a4985fc Mon Sep 17 00:00:00 2001 From: John Millikin Date: Sun, 18 Sep 2022 15:13:42 +0900 Subject: [PATCH 0009/1080] Adjust `tcp_quickack` feature to allow other `os::linux::net` features. --- library/std/src/os/android/net.rs | 4 ++-- library/std/src/os/linux/net.rs | 4 ++-- library/std/src/os/net/linux_ext/mod.rs | 9 +++++++++ library/std/src/os/net/{ => linux_ext}/tcp.rs | 0 library/std/src/os/net/{ => linux_ext}/tests.rs | 3 +-- library/std/src/os/net/mod.rs | 9 +++------ 6 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 library/std/src/os/net/linux_ext/mod.rs rename library/std/src/os/net/{ => linux_ext}/tcp.rs (100%) rename library/std/src/os/net/{ => linux_ext}/tests.rs (88%) diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index ff96125c37bdc..53ef7e114c071 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -1,4 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! Android-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index ff96125c37bdc..d67b369be27b1 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -1,4 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! Linux-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs new file mode 100644 index 0000000000000..9bd66f3bb80d3 --- /dev/null +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -0,0 +1,9 @@ +//! Linux and Android-specific networking functionality. + +#![doc(cfg(any(target_os = "linux", target_os = "android")))] + +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub(crate) mod tcp; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs similarity index 100% rename from library/std/src/os/net/tcp.rs rename to library/std/src/os/net/linux_ext/tcp.rs diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/linux_ext/tests.rs similarity index 88% rename from library/std/src/os/net/tests.rs rename to library/std/src/os/net/linux_ext/tests.rs index 4704e3156913c..2db4deed03630 100644 --- a/library/std/src/os/net/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,9 +1,8 @@ -#[cfg(any(target_os = "android", target_os = "linux",))] #[test] fn quickack() { use crate::{ net::{test::next_test_ip4, TcpListener, TcpStream}, - os::net::tcp::TcpStreamExt, + os::net::linux_ext::tcp::TcpStreamExt, }; macro_rules! t { diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs index d6d84d24ec489..5ec267c41e97c 100644 --- a/library/std/src/os/net/mod.rs +++ b/library/std/src/os/net/mod.rs @@ -1,7 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! OS-specific networking functionality. -#![unstable(feature = "tcp_quickack", issue = "96256")] -#![doc(cfg(any(target_os = "linux", target_os = "android",)))] -pub mod tcp; -#[cfg(test)] -mod tests; +#[cfg(any(target_os = "linux", target_os = "android", doc))] +pub(super) mod linux_ext; From 8f1e6eba343452ac48412f11d57aa7d206c8c3dd Mon Sep 17 00:00:00 2001 From: John Millikin Date: Sun, 18 Sep 2022 16:20:11 +0900 Subject: [PATCH 0010/1080] Move `unix_socket_abstract` feature API to `SocketAddrExt`. --- library/std/src/os/android/net.rs | 5 ++ library/std/src/os/linux/net.rs | 5 ++ library/std/src/os/net/linux_ext/addr.rs | 64 ++++++++++++++++ library/std/src/os/net/linux_ext/mod.rs | 3 + library/std/src/os/unix/net/addr.rs | 93 +++++++----------------- library/std/src/os/unix/net/tests.rs | 36 +++++---- 6 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 library/std/src/os/net/linux_ext/addr.rs diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index 53ef7e114c071..7cecd1bbfaa95 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -1,4 +1,9 @@ //! Android-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index d67b369be27b1..94081c8dd31c5 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -1,4 +1,9 @@ //! Linux-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs new file mode 100644 index 0000000000000..df3fc8e6a3b66 --- /dev/null +++ b/library/std/src/os/net/linux_ext/addr.rs @@ -0,0 +1,64 @@ +//! Linux and Android-specific extensions to socket addresses. + +use crate::os::unix::net::SocketAddr; +use crate::sealed::Sealed; + +/// Platform-specific extensions to [`SocketAddr`]. +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub trait SocketAddrExt: Sealed { + /// Creates a Unix socket address in the abstract namespace. + /// + /// The abstract namespace is a Linux-specific extension that allows Unix + /// sockets to be bound without creating an entry in the filesystem. + /// Abstract sockets are unaffected by filesystem layout or permissions, + /// and no cleanup is necessary when the socket is closed. + /// + /// An abstract socket address name may contain any bytes, including zero. + /// + /// # Errors + /// + /// Returns an error if the name is longer than `SUN_LEN - 1`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_name(b"hidden")?; + /// let listener = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>; + + /// Returns the contents of this address if it is in the abstract namespace. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let name = b"hidden"; + /// let name_addr = SocketAddr::from_abstract_name(name)?; + /// let socket = UnixListener::bind_addr(&name_addr)?; + /// let local_addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(local_addr.as_abstract_name(), Some(&name[..])); + /// Ok(()) + /// } + /// ``` + fn as_abstract_name(&self) -> Option<&[u8]>; +} diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs index 9bd66f3bb80d3..318ebacfd7a08 100644 --- a/library/std/src/os/net/linux_ext/mod.rs +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -2,6 +2,9 @@ #![doc(cfg(any(target_os = "linux", target_os = "android")))] +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub(crate) mod addr; + #[unstable(feature = "tcp_quickack", issue = "96256")] pub(crate) mod tcp; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 094085e19428c..81ac829d21bc8 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -1,6 +1,9 @@ use crate::ffi::OsStr; +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +use crate::os::net::linux_ext; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; +use crate::sealed::Sealed; use crate::sys::cvt; use crate::{fmt, io, mem, ptr}; @@ -224,31 +227,6 @@ impl SocketAddr { if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } } - /// Returns the contents of this address if it is an abstract namespace - /// without the leading null byte. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let namespace = b"hidden"; - /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; - /// let socket = UnixListener::bind_addr(&namespace_addr)?; - /// let local_addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn as_abstract_namespace(&self) -> Option<&[u8]> { - if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } - } - fn address(&self) -> AddressKind<'_> { let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; @@ -265,62 +243,41 @@ impl SocketAddr { AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) } } +} - /// Creates an abstract domain socket address from a namespace - /// - /// An abstract address does not create a file unlike traditional path-based - /// Unix sockets. The advantage of this is that the address will disappear when - /// the socket bound to it is closed, so no filesystem clean up is required. - /// - /// The leading null byte for the abstract namespace is automatically added. - /// - /// This is a Linux-specific extension. See more at [`unix(7)`]. - /// - /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html - /// - /// # Errors - /// - /// This will return an error if the given namespace is too long - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; - /// let listener = match UnixListener::bind_addr(&addr) { - /// Ok(sock) => sock, - /// Err(err) => { - /// println!("Couldn't bind: {err:?}"); - /// return Err(err); - /// } - /// }; - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result { +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl Sealed for SocketAddr {} + +#[doc(cfg(any(target_os = "android", target_os = "linux")))] +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl linux_ext::addr::SocketAddrExt for SocketAddr { + fn as_abstract_name(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } + } + + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>, + { + let name = name.as_ref(); unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - if namespace.len() + 1 > addr.sun_path.len() { + if name.len() + 1 > addr.sun_path.len() { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - "namespace must be shorter than SUN_LEN", + "abstract socket name must be shorter than SUN_LEN", )); } crate::ptr::copy_nonoverlapping( - namespace.as_ptr(), + name.as_ptr(), addr.sun_path.as_mut_ptr().add(1) as *mut u8, - namespace.len(), + name.len(), ); - let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; + let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t; SocketAddr::from_parts(addr, len) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index e4499f9b6a6dc..37fcfa8446b0e 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,6 +7,12 @@ use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; +#[cfg(target_os = "android")] +use crate::os::android::net::SocketAddrExt; + +#[cfg(target_os = "linux")] +use crate::os::linux::net::SocketAddrExt; + macro_rules! or_panic { ($e:expr) => { match $e { @@ -404,7 +410,7 @@ fn test_abstract_stream_connect() { let msg1 = b"hello"; let msg2 = b"world"; - let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace")); + let socket_addr = or_panic!(SocketAddr::from_abstract_name(b"name")); let listener = or_panic!(UnixListener::bind_addr(&socket_addr)); let thread = thread::spawn(move || { @@ -418,7 +424,7 @@ fn test_abstract_stream_connect() { let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr)); let peer = or_panic!(stream.peer_addr()); - assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace"); + assert_eq!(peer.as_abstract_name().unwrap(), b"name"); or_panic!(stream.write_all(msg1)); let mut buf = vec![]; @@ -432,7 +438,7 @@ fn test_abstract_stream_connect() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_stream_iter() { - let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden")); + let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden")); let listener = or_panic!(UnixListener::bind_addr(&addr)); let thread = thread::spawn(move || { @@ -454,13 +460,13 @@ fn test_abstract_stream_iter() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_bind_send_to_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1")); let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let local = or_panic!(sock1.local_addr()); - assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(local.as_abstract_name().unwrap(), b"ns1"); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns2")); let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); let msg = b"hello world"; @@ -469,13 +475,13 @@ fn test_abstract_datagram_bind_send_to_addr() { let (len, addr) = or_panic!(sock2.recv_from(&mut buf)); assert_eq!(msg, &buf[..]); assert_eq!(len, 11); - assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(addr.as_abstract_name().unwrap(), b"ns1"); } #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_connect_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3")); let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let sock = or_panic!(UnixDatagram::unbound()); @@ -489,7 +495,7 @@ fn test_abstract_datagram_connect_addr() { assert_eq!(addr.is_unnamed(), true); assert_eq!(msg, &buf[..]); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns4")); let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); or_panic!(sock.connect_addr(&addr2)); @@ -499,8 +505,8 @@ fn test_abstract_datagram_connect_addr() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_too_long() { - match SocketAddr::from_abstract_namespace( +fn test_abstract_name_too_long() { + match SocketAddr::from_abstract_name( b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\ opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\ jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", @@ -513,11 +519,11 @@ fn test_abstract_namespace_too_long() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_no_pathname_and_not_unnamed() { - let namespace = b"local"; - let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..])); +fn test_abstract_no_pathname_and_not_unnamed() { + let name = b"local"; + let addr = or_panic!(SocketAddr::from_abstract_name(name)); assert_eq!(addr.as_pathname(), None); - assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..])); + assert_eq!(addr.as_abstract_name(), Some(&name[..])); assert_eq!(addr.is_unnamed(), false); } From a052f2cce1df8ac5ac9fcd104c948545f8b5f2f4 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 20 Sep 2022 11:55:07 +0000 Subject: [PATCH 0011/1080] Add the `#[derive_const]` attribute --- .../src/cfg_accessible.rs | 1 + compiler/rustc_builtin_macros/src/derive.rs | 9 ++--- .../src/deriving/bounds.rs | 2 ++ .../src/deriving/clone.rs | 2 ++ .../src/deriving/cmp/eq.rs | 2 ++ .../src/deriving/cmp/ord.rs | 2 ++ .../src/deriving/cmp/partial_eq.rs | 2 ++ .../src/deriving/cmp/partial_ord.rs | 2 ++ .../src/deriving/debug.rs | 2 ++ .../src/deriving/decodable.rs | 2 ++ .../src/deriving/default.rs | 2 ++ .../src/deriving/encodable.rs | 2 ++ .../src/deriving/generic/mod.rs | 6 ++-- .../rustc_builtin_macros/src/deriving/hash.rs | 2 ++ .../rustc_builtin_macros/src/deriving/mod.rs | 35 ++++++++++++------- compiler/rustc_builtin_macros/src/lib.rs | 3 +- compiler/rustc_expand/src/base.rs | 4 ++- compiler/rustc_expand/src/expand.rs | 13 +++---- compiler/rustc_expand/src/proc_macro.rs | 1 + compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/macros/mod.rs | 13 +++++++ library/core/src/prelude/v1.rs | 4 +++ library/std/src/prelude/v1.rs | 4 +++ src/test/ui/issues/issue-32655.stderr | 14 ++++++-- .../const_derives/derive-const-gate.rs | 4 +++ .../const_derives/derive-const-gate.stderr | 11 ++++++ .../derive-const-non-const-type.rs | 13 +++++++ .../derive-const-non-const-type.stderr | 14 ++++++++ .../const_derives/derive-const-use.rs | 19 ++++++++++ 30 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index cb5359dd1e27e..86df3c44eb334 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -34,6 +34,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { let template = AttributeTemplate { list: Some("path"), ..Default::default() }; let attr = &ecx.attribute(meta_item.clone()); diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index e0fb7affb3498..01f237e6ab5fa 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -10,7 +10,7 @@ use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -pub(crate) struct Expander; +pub(crate) struct Expander(pub bool); impl MultiItemModifier for Expander { fn expand( @@ -19,6 +19,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _: bool, ) -> ExpandResult, Annotatable> { let sess = ecx.sess; if report_bad_target(sess, &item, span) { @@ -58,20 +59,20 @@ impl MultiItemModifier for Expander { report_path_args(sess, &meta); meta.path }) - .map(|path| (path, dummy_annotatable(), None)) + .map(|path| (path, dummy_annotatable(), None, self.0)) .collect(); // Do not configure or clone items unless necessary. match &mut resolutions[..] { [] => {} - [(_, first_item, _), others @ ..] => { + [(_, first_item, ..), others @ ..] => { *first_item = cfg_eval( sess, features, item.clone(), ecx.current_expansion.lint_node_id, ); - for (_, item, _) in others { + for (_, item, _, _) in others { *item = first_item.clone(); } } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 77e0b6c55a80e..3a5b7ecde8a9e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -12,6 +12,7 @@ pub fn expand_deriving_copy( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let trait_def = TraitDef { span, @@ -21,6 +22,7 @@ pub fn expand_deriving_copy( supports_unions: true, methods: Vec::new(), associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index c7f2d95e72f0c..6d9be879cd19f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_clone( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // The simple form is `fn clone(&self) -> Self { *self }`, possibly with // some additional `AssertParamIsClone` assertions. @@ -86,6 +87,7 @@ pub fn expand_deriving_clone( combine_substructure: substructure, }], associated_types: Vec::new(), + is_const, }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 5b556c5c9b9d1..9c01314112a59 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -15,6 +15,7 @@ pub fn expand_deriving_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let span = cx.with_def_site_ctxt(span); let inline = cx.meta_word(span, sym::inline); @@ -41,6 +42,7 @@ pub fn expand_deriving_eq( })), }], associated_types: Vec::new(), + is_const, }; super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 7262586955811..d5c81c387834e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let inline = cx.meta_word(span, sym::inline); let attrs = thin_vec![cx.attribute(inline)]; @@ -33,6 +34,7 @@ pub fn expand_deriving_ord( combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 42ee65b570a2a..11b838a076c6d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_partial_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let base = true; @@ -88,6 +89,7 @@ pub fn expand_deriving_partial_eq( supports_unions: false, methods, associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 516892aeda96f..107a01190bb40 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_partial_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let ordering_ty = Path(path_std!(cmp::Ordering)); let ret_ty = @@ -42,6 +43,7 @@ pub fn expand_deriving_partial_ord( supports_unions: false, methods: vec![partial_cmp_def], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 4af7fd8165388..cf977c0824d8d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_debug( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // &mut ::std::fmt::Formatter let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut); @@ -36,6 +37,7 @@ pub fn expand_deriving_debug( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 7174dbbe7ea8b..a27a068f31a27 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_rustc_decodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__D; @@ -54,6 +55,7 @@ pub fn expand_deriving_rustc_decodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index a94c8a996e642..35b23f2f8f31a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_default( mitem: &ast::MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); @@ -46,6 +47,7 @@ pub fn expand_deriving_default( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index b220e54238f46..f06cf0c56bc70 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -100,6 +100,7 @@ pub fn expand_deriving_rustc_encodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__S; @@ -138,6 +139,7 @@ pub fn expand_deriving_rustc_encodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3cc160adb5397..78dbe8e5979e2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -171,7 +171,7 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use std::cell::RefCell; use std::iter; use std::vec; @@ -200,6 +200,8 @@ pub struct TraitDef<'a> { pub methods: Vec>, pub associated_types: Vec<(Ident, Ty)>, + + pub is_const: bool, } pub struct MethodDef<'a> { @@ -726,7 +728,7 @@ impl<'a> TraitDef<'a> { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, generics: trait_generics, of_trait: opt_trait_ref, self_ty: self_type, diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index f1f02e7ce7787..ef3da94f9e3f5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_hash( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let path = Path::new_(pathvec_std!(hash::Hash), vec![], PathKind::Std); @@ -38,6 +39,7 @@ pub fn expand_deriving_hash( })), }], associated_types: Vec::new(), + is_const, }; hash_trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index a65d0bad6de80..5b89da91d4278 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -38,9 +38,10 @@ pub mod partial_ord; pub mod generic; -pub(crate) struct BuiltinDerive( - pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), -); +pub(crate) type BuiltinDeriveFn = + fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); + +pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn); impl MultiItemModifier for BuiltinDerive { fn expand( @@ -49,6 +50,7 @@ impl MultiItemModifier for BuiltinDerive { span: Span, meta_item: &MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable> { // FIXME: Built-in derives often forget to give spans contexts, // so we are doing it here in a centralized way. @@ -57,21 +59,28 @@ impl MultiItemModifier for BuiltinDerive { match item { Annotatable::Stmt(stmt) => { if let ast::StmtKind::Item(item) = stmt.into_inner().kind { - (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { - // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' - // to the function - items.push(Annotatable::Stmt(P(ast::Stmt { - id: ast::DUMMY_NODE_ID, - kind: ast::StmtKind::Item(a.expect_item()), - span, - }))); - }); + (self.0)( + ecx, + span, + meta_item, + &Annotatable::Item(item), + &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + }))); + }, + is_derive_const, + ); } else { unreachable!("should have already errored on non-item statement") } } _ => { - (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const); } } ExpandResult::Ready(items) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 8aeb3b82a9cd0..ab1fcde16865f 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -97,7 +97,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, - derive: derive::Expander, + derive: derive::Expander(false), + derive_const: derive::Expander(true), global_allocator: global_allocator::expand, test: test::expand_test, test_case: test::expand_test_case, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e1da3ecdec7f4..b3d187848be9c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -248,6 +248,7 @@ pub trait MultiItemModifier { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable>; } @@ -261,6 +262,7 @@ where span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { ExpandResult::Ready(self(ecx, span, meta_item, item)) } @@ -871,7 +873,7 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; -pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>)>; +pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>, bool)>; pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c2add852a0679..a63b59d31cfd9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -319,6 +319,7 @@ pub enum InvocationKind { }, Derive { path: ast::Path, + is_const: bool, item: Annotatable, }, } @@ -460,13 +461,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(path, item, _exts)| { + .map(|(path, item, _exts, is_const)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = LocalExpnId::fresh_empty(); derive_invocations.push(( Invocation { - kind: InvocationKind::Derive { path, item }, + kind: InvocationKind::Derive { path, item, is_const }, fragment_kind, expansion_data: ExpansionData { id: expn_id, @@ -699,7 +700,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::LegacyAttr(expander) => { match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) { Ok(meta) => { - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, false) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. @@ -731,19 +732,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, - InvocationKind::Derive { path, item } => match ext { + InvocationKind::Derive { path, item, is_const } => match ext { SyntaxExtensionKind::Derive(expander) | SyntaxExtensionKind::LegacyDerive(expander) => { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, is_const) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. return ExpandResult::Retry(Invocation { - kind: InvocationKind::Derive { path: meta.path, item }, + kind: InvocationKind::Derive { path: meta.path, item, is_const }, ..invoc }); } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 1a2ab9d190ebd..e9a6919206894 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -112,6 +112,7 @@ impl MultiItemModifier for DeriveProcMacro { span: Span, _meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index dafa10e9e0026..8ef99e5ef968a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -356,7 +356,7 @@ impl<'a> ResolverExpand for Resolver<'a> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() { if opt_ext.is_none() { *opt_ext = Some( match self.resolve_macro_path( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 562360130e92f..0a9570979d5af 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -614,6 +614,7 @@ symbols! { deref_mut, deref_target, derive, + derive_const, derive_default_enum, destruct, destructuring_assignment, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index fd96e1ff77d53..e504db8143447 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1464,6 +1464,19 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro used to apply derive macros for implementing traits + /// in a const context. + /// + /// See [the reference] for more info. + /// + /// [the reference]: ../../../reference/attributes/derive.html + #[unstable(feature = "derive_const", issue = "none")] + #[rustc_builtin_macro] + #[cfg(not(bootstrap))] + pub macro derive_const($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to turn it into a unit test. /// /// See [the reference] for more info. diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index b566e211cd89d..f52c06d7eadc2 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -78,6 +78,10 @@ pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use crate::macros::builtin::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::derive_const; + #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0226c4d7a2581..93f4a17bbfca3 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -62,6 +62,10 @@ pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::prelude::v1::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use core::prelude::v1::derive_const; + // Do not `doc(no_inline)` either. #[unstable( feature = "cfg_accessible", diff --git a/src/test/ui/issues/issue-32655.stderr b/src/test/ui/issues/issue-32655.stderr index 2d9ce430a462d..5a758c7002b92 100644 --- a/src/test/ui/issues/issue-32655.stderr +++ b/src/test/ui/issues/issue-32655.stderr @@ -2,18 +2,28 @@ error: cannot find attribute `derive_Clone` in this scope --> $DIR/issue-32655.rs:3:11 | LL | #[derive_Clone] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `derive_const` ... LL | foo!(); | ------ in this macro invocation | + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | pub macro derive_const($item:item) { + | ---------------------- similarly named attribute macro `derive_const` defined here + | = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find attribute `derive_Clone` in this scope --> $DIR/issue-32655.rs:15:7 | LL | #[derive_Clone] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `derive_const` + | + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | pub macro derive_const($item:item) { + | ---------------------- similarly named attribute macro `derive_const` defined here error: aborting due to 2 previous errors diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs new file mode 100644 index 0000000000000..348ca0ab1906b --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs @@ -0,0 +1,4 @@ +#[derive_const(Default)] //~ ERROR use of unstable library feature +pub struct S; + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr new file mode 100644 index 0000000000000..cc9bdd2715f70 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'derive_const' + --> $DIR/derive-const-gate.rs:1:3 + | +LL | #[derive_const(Default)] + | ^^^^^^^^^^^^ + | + = help: add `#![feature(derive_const)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs new file mode 100644 index 0000000000000..92843a8a2da48 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs @@ -0,0 +1,13 @@ +#![feature(derive_const)] + +pub struct A; + +impl Default for A { + fn default() -> A { A } +} + +#[derive_const(Default)] +pub struct S(A); +//~^ cannot call non-const fn + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr new file mode 100644 index 0000000000000..d463c774e289e --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr @@ -0,0 +1,14 @@ +error[E0015]: cannot call non-const fn `::default` in constant functions + --> $DIR/derive-const-non-const-type.rs:10:14 + | +LL | #[derive_const(Default)] + | ------- in this derive macro expansion +LL | pub struct S(A); + | ^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs new file mode 100644 index 0000000000000..d1fbeac8598e4 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs @@ -0,0 +1,19 @@ +// check-pass +#![feature(const_trait_impl, const_cmp, const_default_impls, derive_const)] + +pub struct A; + +impl const Default for A { + fn default() -> A { A } +} + +impl const PartialEq for A { + fn eq(&self, _: &A) -> bool { true } +} + +#[derive_const(Default, PartialEq)] +pub struct S((), A); + +const _: () = assert!(S((), A) == S::default()); + +fn main() {} From 6630c146f213088dff0d4ebd8dad90591544c169 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 23 Sep 2022 17:06:28 -0700 Subject: [PATCH 0012/1080] Implement the `+whole-archive` modifier for `wasm-ld` This implements the `Linker::{link_whole_staticlib,link_whole_rlib}` methods for the `WasmLd` linker used on wasm targets. Previously these methods were noops since I think historically `wasm-ld` did not have support for `--whole-archive` but nowadays it does, so the flags are passed through. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e0bd7a33f7373..6e59a4f64db25 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1265,11 +1265,11 @@ impl<'a> Linker for WasmLd<'a> { } fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); + self.cmd.arg("--whole-archive").arg("-l").arg(lib).arg("--no-whole-archive"); } fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); + self.cmd.arg("--whole-archive").arg(lib).arg("--no-whole-archive"); } fn gc_sections(&mut self, _keep_metadata: bool) { From 12c15a2bfe9f4144003366bc72e10387fbef9aab Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 29 Sep 2022 14:23:47 +0200 Subject: [PATCH 0013/1080] Split out from_u32_unchecked from const_char_convert It relies on the Option::unwrap function which is not const-stable (yet). --- library/core/src/char/convert.rs | 1 - library/core/src/char/methods.rs | 2 +- library/core/src/char/mod.rs | 2 +- library/core/src/lib.rs | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7c5f82f5ea49d..f1a51a550f579 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -18,7 +18,6 @@ pub(super) const fn from_u32(i: u32) -> Option { } /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] #[inline] #[must_use] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index b7a63b7c67566..4416602c0bda0 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -183,7 +183,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index b34a7121631c1..bb3a96a19fb2c 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -120,7 +120,7 @@ pub const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c3df0d4f9b91a..f98ea6985b026 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -105,6 +105,7 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_char_convert)] +#![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] #![feature(const_discriminant)] From 176c44c08ed797b5ddea893a0a292a7bee0d8f8c Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 29 Sep 2022 14:24:29 +0200 Subject: [PATCH 0014/1080] Stabilize const_char_convert --- library/core/src/char/methods.rs | 6 +++--- library/core/src/char/mod.rs | 4 ++-- library/core/src/lib.rs | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 4416602c0bda0..275e5cff4193c 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -140,7 +140,7 @@ impl char { /// assert_eq!(None, c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_u32(i: u32) -> Option { @@ -241,7 +241,7 @@ impl char { /// let _c = char::from_digit(1, 37); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { @@ -338,7 +338,7 @@ impl char { /// let _ = '1'.to_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index bb3a96a19fb2c..55552376280ab 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -110,7 +110,7 @@ pub fn decode_utf16>(iter: I) -> DecodeUtf16 Option { @@ -130,7 +130,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char { /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f98ea6985b026..bb162b6d0f642 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -104,7 +104,6 @@ #![feature(const_black_box)] #![feature(const_caller_location)] #![feature(const_cell_into_inner)] -#![feature(const_char_convert)] #![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] From 3694429d09a2586cea5c769cddee10cd70f39d84 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 29 Jul 2022 23:06:13 +0400 Subject: [PATCH 0015/1080] recover wrong-cased `use`s (`Use`, `USE`, etc) --- compiler/rustc_parse/src/parser/item.rs | 25 ++++++++++++++--- compiler/rustc_parse/src/parser/mod.rs | 27 +++++++++++++++++++ .../ui/parser/item-kw-case-mismatch.fixed | 7 +++++ src/test/ui/parser/item-kw-case-mismatch.rs | 7 +++++ .../ui/parser/item-kw-case-mismatch.stderr | 14 ++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/parser/item-kw-case-mismatch.fixed create mode 100644 src/test/ui/parser/item-kw-case-mismatch.rs create mode 100644 src/test/ui/parser/item-kw-case-mismatch.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 25425fbb2c6a6..34c7d4ec4815d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -143,8 +143,15 @@ impl<'a> Parser<'a> { let lo = self.token.span; let vis = self.parse_visibility(FollowedByType::No)?; let mut def = self.parse_defaultness(); - let kind = - self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?; + let kind = self.parse_item_kind( + &mut attrs, + mac_allowed, + lo, + &vis, + &mut def, + fn_parse_mode, + false, + )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); let span = lo.to(self.prev_token.span); @@ -205,11 +212,12 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, + kw_case_insensitive: bool, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; let mut def = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword(kw::Use) { + let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { self.parse_use_item()? } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM @@ -286,6 +294,17 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); + } else if self.isnt_macro_invocation() && !kw_case_insensitive { + // Recover wrong cased keywords + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + &mut def(), + fn_parse_mode, + true, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2aebaf7c3af2a..b82ce90129fee 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -616,6 +616,33 @@ impl<'a> Parser<'a> { } } + /// Eats a keyword, optionally ignoring the case. + /// If the case differs (and is ignored) an error is issued. + /// This is useful for recovery. + fn eat_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + if self.eat_keyword(kw) { + return true; + } + + if case_insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + self + .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case")) + .span_suggestion( + ident.span, + "write it in the correct case", + kw, + Applicability::MachineApplicable + ).emit(); + + self.bump(); + return true; + } + + false + } + fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); diff --git a/src/test/ui/parser/item-kw-case-mismatch.fixed b/src/test/ui/parser/item-kw-case-mismatch.fixed new file mode 100644 index 0000000000000..d99f89ccef5b4 --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.fixed @@ -0,0 +1,7 @@ +// run-rustfix +#![allow(unused_imports)] + +fn main() {} + +use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case +use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.rs b/src/test/ui/parser/item-kw-case-mismatch.rs new file mode 100644 index 0000000000000..605552b5f147f --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.rs @@ -0,0 +1,7 @@ +// run-rustfix +#![allow(unused_imports)] + +fn main() {} + +Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case +USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.stderr b/src/test/ui/parser/item-kw-case-mismatch.stderr new file mode 100644 index 0000000000000..aebbc9d558f27 --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.stderr @@ -0,0 +1,14 @@ +error: keyword `use` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:6:1 + | +LL | Use std::ptr::read; + | ^^^ help: write it in the correct case (notice the capitalization): `use` + +error: keyword `use` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:7:1 + | +LL | USE std::ptr::write; + | ^^^ help: write it in the correct case: `use` + +error: aborting due to 2 previous errors + From 38b086524875c1ed9904f94eca64a162f7572dae Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 13 Sep 2022 22:48:29 +0400 Subject: [PATCH 0016/1080] Recover wrong cased keywords starting functions --- compiler/rustc_ast/src/token.rs | 9 +++ compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 79 ++++++++++--------- compiler/rustc_parse/src/parser/mod.rs | 30 +++++-- compiler/rustc_parse/src/parser/ty.rs | 4 +- .../ui/parser/item-kw-case-mismatch.fixed | 27 +++++++ src/test/ui/parser/item-kw-case-mismatch.rs | 27 +++++++ .../ui/parser/item-kw-case-mismatch.stderr | 78 +++++++++++++++++- 8 files changed, 205 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 99034799b3c31..3f7e32f4fae66 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -618,6 +618,15 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } + /// Returns `true` if the token is a given keyword, `kw` or if `case_insensitive` is true and this token is an identifier equal to `kw` ignoring the case. + pub fn is_keyword_case(&self, kw: Symbol, case_insensitive: bool) -> bool { + self.is_keyword(kw) + || (case_insensitive + && self.is_non_raw_ident_where(|id| { + id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() + })) + } + pub fn is_path_segment_keyword(&self) -> bool { self.is_non_raw_ident_where(Ident::is_path_segment_keyword) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8b328e593ae80..e08d09a4d9f3e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2024,7 +2024,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness() + self.parse_asyncness(false) } else { Async::No }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 34c7d4ec4815d..4e2fc513ac502 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -34,7 +34,7 @@ impl<'a> Parser<'a> { /// Parses a `mod { ... }` or `mod ;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -215,14 +215,14 @@ impl<'a> Parser<'a> { kw_case_insensitive: bool, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; - let mut def = || mem::replace(def, Defaultness::Final); + let mut def_ = || mem::replace(def, Defaultness::Final); let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final) { + } else if self.check_fn_front_matter(def_final, kw_case_insensitive) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) + (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -233,7 +233,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -242,15 +242,15 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness() { + } else if let Const::Yes(const_span) = self.parse_constness(false) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` - self.recover_const_impl(const_span, attrs, def())? + self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(def(), ty, expr)) + (ident, ItemKind::Const(def_(), ty, expr)) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -259,7 +259,7 @@ impl<'a> Parser<'a> { || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) { // IMPL ITEM - self.parse_item_impl(attrs, def())? + self.parse_item_impl(attrs, def_())? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -267,7 +267,7 @@ impl<'a> Parser<'a> { self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { // TYPE ITEM - self.parse_type_alias(def())? + self.parse_type_alias(def_())? } else if self.eat_keyword(kw::Enum) { // ENUM ITEM self.parse_item_enum()? @@ -295,16 +295,10 @@ impl<'a> Parser<'a> { self.recover_missing_kw_before_item()?; return Ok(None); } else if self.isnt_macro_invocation() && !kw_case_insensitive { + _ = def_; + // Recover wrong cased keywords - return self.parse_item_kind( - attrs, - macros_allowed, - lo, - vis, - &mut def(), - fn_parse_mode, - true, - ); + return self.parse_item_kind(attrs, macros_allowed, lo, vis, def, fn_parse_mode, true); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -557,7 +551,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -571,7 +565,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(); + let constness = self.parse_constness(false); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -815,7 +809,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -1764,7 +1758,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false) { + let err = if self.check_fn_front_matter(false, false) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -2153,7 +2147,11 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { + pub(super) fn check_fn_front_matter( + &mut self, + check_pub: bool, + kw_case_insensitive: bool, + ) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2163,23 +2161,30 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword(kw::Fn) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, kw_case_insensitive) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword(kw)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, kw_case_insensitive)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword(kw::Fn) + t.is_keyword_case(kw::Fn, kw_case_insensitive) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || t.is_non_raw_ident_where(|i| quals.contains(&i.name) - // Rule out 2015 `const async: T = val`. - && i.is_reserved() + || ( + ( + t.is_non_raw_ident_where(|i| + quals.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + ) + || kw_case_insensitive + && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) + ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword(kw::Extern) + || self.check_keyword_case(kw::Extern, kw_case_insensitive) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword(kw::Fn)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, kw_case_insensitive)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2195,22 +2200,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(); + let constness = self.parse_constness(true); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(); + let asyncness = self.parse_asyncness(true); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(true); let ext_start_sp = self.token.span; - let ext = self.parse_extern(); + let ext = self.parse_extern(true); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword(kw::Fn) { + if !self.eat_keyword_case(kw::Fn, true) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b82ce90129fee..073c51ac120f6 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -604,6 +604,20 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } + fn check_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + if self.check_keyword(kw) { + return true; + } + + if case_insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + true + } else { + false + } + } + /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. @@ -1122,8 +1136,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self) -> Async { - if self.eat_keyword(kw::Async) { + fn parse_asyncness(&mut self, case_insensitive: bool) -> Async { + if self.eat_keyword_case(kw::Async, case_insensitive) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1132,8 +1146,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self) -> Unsafe { - if self.eat_keyword(kw::Unsafe) { + fn parse_unsafety(&mut self, case_insensitive: bool) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case_insensitive) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1141,10 +1155,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self) -> Const { + fn parse_constness(&mut self, case_insensitive: bool) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword(kw::Const) + && self.eat_keyword_case(kw::Const, case_insensitive) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1399,8 +1413,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self) -> Extern { - if self.eat_keyword(kw::Extern) { + fn parse_extern(&mut self, case_insensitive: bool) -> Extern { + if self.eat_keyword_case(kw::Extern, case_insensitive) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2a8512acf8cfa..984e303e577ec 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -267,7 +267,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false) { + } else if self.check_fn_front_matter(false, false) { // Function pointer type self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { @@ -275,7 +275,7 @@ impl<'a> Parser<'a> { // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false) { + if self.check_fn_front_matter(false, false) { self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; diff --git a/src/test/ui/parser/item-kw-case-mismatch.fixed b/src/test/ui/parser/item-kw-case-mismatch.fixed index d99f89ccef5b4..1794268f260e1 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.fixed +++ b/src/test/ui/parser/item-kw-case-mismatch.fixed @@ -1,7 +1,34 @@ // run-rustfix +// edition:2018 #![allow(unused_imports)] fn main() {} use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case + +async fn _a() {} +//~^ ERROR keyword `fn` is written in a wrong case + +fn _b() {} +//~^ ERROR keyword `fn` is written in a wrong case + +async fn _c() {} +//~^ ERROR keyword `async` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +async fn _d() {} +//~^ ERROR keyword `async` is written in a wrong case + +const unsafe fn _e() {} +//~^ ERROR keyword `const` is written in a wrong case +//~| ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +unsafe extern fn _f() {} +//~^ ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `extern` is written in a wrong case + +extern "C" fn _g() {} +//~^ ERROR keyword `extern` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.rs b/src/test/ui/parser/item-kw-case-mismatch.rs index 605552b5f147f..ac8390efdb9a3 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.rs +++ b/src/test/ui/parser/item-kw-case-mismatch.rs @@ -1,7 +1,34 @@ // run-rustfix +// edition:2018 #![allow(unused_imports)] fn main() {} Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case + +async Fn _a() {} +//~^ ERROR keyword `fn` is written in a wrong case + +Fn _b() {} +//~^ ERROR keyword `fn` is written in a wrong case + +aSYNC fN _c() {} +//~^ ERROR keyword `async` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +Async fn _d() {} +//~^ ERROR keyword `async` is written in a wrong case + +CONST UNSAFE FN _e() {} +//~^ ERROR keyword `const` is written in a wrong case +//~| ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +unSAFE EXTern fn _f() {} +//~^ ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `extern` is written in a wrong case + +EXTERN "C" FN _g() {} +//~^ ERROR keyword `extern` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.stderr b/src/test/ui/parser/item-kw-case-mismatch.stderr index aebbc9d558f27..e66dae825f9c4 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.stderr +++ b/src/test/ui/parser/item-kw-case-mismatch.stderr @@ -1,14 +1,86 @@ error: keyword `use` is written in a wrong case - --> $DIR/item-kw-case-mismatch.rs:6:1 + --> $DIR/item-kw-case-mismatch.rs:7:1 | LL | Use std::ptr::read; | ^^^ help: write it in the correct case (notice the capitalization): `use` error: keyword `use` is written in a wrong case - --> $DIR/item-kw-case-mismatch.rs:7:1 + --> $DIR/item-kw-case-mismatch.rs:8:1 | LL | USE std::ptr::write; | ^^^ help: write it in the correct case: `use` -error: aborting due to 2 previous errors +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:10:7 + | +LL | async Fn _a() {} + | ^^ help: write it in the correct case (notice the capitalization): `fn` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:13:1 + | +LL | Fn _b() {} + | ^^ help: write it in the correct case (notice the capitalization): `fn` + +error: keyword `async` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:16:1 + | +LL | aSYNC fN _c() {} + | ^^^^^ help: write it in the correct case: `async` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:16:7 + | +LL | aSYNC fN _c() {} + | ^^ help: write it in the correct case: `fn` + +error: keyword `async` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:20:1 + | +LL | Async fn _d() {} + | ^^^^^ help: write it in the correct case: `async` + +error: keyword `const` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:1 + | +LL | CONST UNSAFE FN _e() {} + | ^^^^^ help: write it in the correct case: `const` + +error: keyword `unsafe` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:7 + | +LL | CONST UNSAFE FN _e() {} + | ^^^^^^ help: write it in the correct case: `unsafe` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:14 + | +LL | CONST UNSAFE FN _e() {} + | ^^ help: write it in the correct case: `fn` + +error: keyword `unsafe` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:28:1 + | +LL | unSAFE EXTern fn _f() {} + | ^^^^^^ help: write it in the correct case: `unsafe` + +error: keyword `extern` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:28:8 + | +LL | unSAFE EXTern fn _f() {} + | ^^^^^^ help: write it in the correct case: `extern` + +error: keyword `extern` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:32:1 + | +LL | EXTERN "C" FN _g() {} + | ^^^^^^ help: write it in the correct case: `extern` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:32:12 + | +LL | EXTERN "C" FN _g() {} + | ^^ help: write it in the correct case: `fn` + +error: aborting due to 14 previous errors From d86f9cd4640c9ad81ad4aec45c358d1931d40f30 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 15 Sep 2022 20:27:23 +0400 Subject: [PATCH 0017/1080] Replace some `bool` params with an enum --- compiler/rustc_ast/src/lib.rs | 1 + compiler/rustc_ast/src/token.rs | 7 +-- compiler/rustc_ast/src/util/case.rs | 6 +++ compiler/rustc_parse/src/parser/expr.rs | 3 +- compiler/rustc_parse/src/parser/item.rs | 63 +++++++++++++------------ compiler/rustc_parse/src/parser/mod.rs | 25 +++++----- compiler/rustc_parse/src/parser/ty.rs | 5 +- 7 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 compiler/rustc_ast/src/util/case.rs diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index eeb7e56e2b124..9c1dfeb1a6142 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_macros; extern crate tracing; pub mod util { + pub mod case; pub mod classify; pub mod comments; pub mod literal; diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 3f7e32f4fae66..5cdd0bf60a9d1 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -5,6 +5,7 @@ pub use TokenKind::*; use crate::ast; use crate::ptr::P; +use crate::util::case::Case; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -618,10 +619,10 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } - /// Returns `true` if the token is a given keyword, `kw` or if `case_insensitive` is true and this token is an identifier equal to `kw` ignoring the case. - pub fn is_keyword_case(&self, kw: Symbol, case_insensitive: bool) -> bool { + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case. + pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { self.is_keyword(kw) - || (case_insensitive + || (case == Case::Insensitive && self.is_non_raw_ident_where(|id| { id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() })) diff --git a/compiler/rustc_ast/src/util/case.rs b/compiler/rustc_ast/src/util/case.rs new file mode 100644 index 0000000000000..1afd7dea7408e --- /dev/null +++ b/compiler/rustc_ast/src/util/case.rs @@ -0,0 +1,6 @@ +/// Whatever to ignore case (`fn` vs `Fn` vs `FN`) or not. Used for recovering. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Case { + Sensitive, + Insensitive, +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e08d09a4d9f3e..6fbcc3fe5a1b9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -33,6 +33,7 @@ use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; +use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -2024,7 +2025,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness(false) + self.parse_asyncness(Case::Sensitive) } else { Async::No }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 4e2fc513ac502..83a5704b6680c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -8,6 +8,7 @@ use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { /// Parses a `mod { ... }` or `mod ;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -150,7 +151,7 @@ impl<'a> Parser<'a> { &vis, &mut def, fn_parse_mode, - false, + Case::Sensitive, )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); @@ -212,14 +213,14 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, - kw_case_insensitive: bool, + case: Case, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; let mut def_ = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { + let info = if self.eat_keyword_case(kw::Use, case) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final, kw_case_insensitive) { + } else if self.check_fn_front_matter(def_final, case) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) @@ -233,7 +234,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -242,7 +243,7 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness(false) { + } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` @@ -294,11 +295,19 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); - } else if self.isnt_macro_invocation() && !kw_case_insensitive { + } else if self.isnt_macro_invocation() && case == Case::Sensitive { _ = def_; // Recover wrong cased keywords - return self.parse_item_kind(attrs, macros_allowed, lo, vis, def, fn_parse_mode, true); + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + def, + fn_parse_mode, + Case::Insensitive, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -551,7 +560,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -565,7 +574,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(false); + let constness = self.parse_constness(Case::Sensitive); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -809,7 +818,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -1758,7 +1767,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false, false) { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -2147,11 +2156,7 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter( - &mut self, - check_pub: bool, - kw_case_insensitive: bool, - ) -> bool { + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2161,12 +2166,12 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword_case(kw::Fn, kw_case_insensitive) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword_case(kw, kw_case_insensitive)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, case)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword_case(kw::Fn, kw_case_insensitive) + t.is_keyword_case(kw::Fn, case) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. || ( ( @@ -2175,16 +2180,16 @@ impl<'a> Parser<'a> { // Rule out 2015 `const async: T = val`. && i.is_reserved() ) - || kw_case_insensitive + || case == Case::Insensitive && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword_case(kw::Extern, kw_case_insensitive) + || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, kw_case_insensitive)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2200,22 +2205,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(true); + let constness = self.parse_constness(Case::Insensitive); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(true); + let asyncness = self.parse_asyncness(Case::Insensitive); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(true); + let unsafety = self.parse_unsafety(Case::Insensitive); let ext_start_sp = self.token.span; - let ext = self.parse_extern(true); + let ext = self.parse_extern(Case::Insensitive); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword_case(kw::Fn, true) { + if !self.eat_keyword_case(kw::Fn, Case::Insensitive) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 073c51ac120f6..11753af7039e8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,7 @@ use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern}; @@ -604,12 +605,12 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } - fn check_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; } - if case_insensitive + if case == Case::Insensitive && let Some((ident, /* is_raw */ false)) = self.token.ident() && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { true @@ -633,12 +634,12 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. - fn eat_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; } - if case_insensitive + if case == Case::Insensitive && let Some((ident, /* is_raw */ false)) = self.token.ident() && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { self @@ -1136,8 +1137,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self, case_insensitive: bool) -> Async { - if self.eat_keyword_case(kw::Async, case_insensitive) { + fn parse_asyncness(&mut self, case: Case) -> Async { + if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1146,8 +1147,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self, case_insensitive: bool) -> Unsafe { - if self.eat_keyword_case(kw::Unsafe, case_insensitive) { + fn parse_unsafety(&mut self, case: Case) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1155,10 +1156,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self, case_insensitive: bool) -> Const { + fn parse_constness(&mut self, case: Case) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword_case(kw::Const, case_insensitive) + && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1413,8 +1414,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self, case_insensitive: bool) -> Extern { - if self.eat_keyword_case(kw::Extern, case_insensitive) { + fn parse_extern(&mut self, case: Case) -> Extern { + if self.eat_keyword_case(kw::Extern, case) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 984e303e577ec..0f3281e528565 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, @@ -267,7 +268,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false, false) { + } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { @@ -275,7 +276,7 @@ impl<'a> Parser<'a> { // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false, false) { + if self.check_fn_front_matter(false, Case::Sensitive) { self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; From 25c1c635e5b7df5b651f587b1427751bfe463339 Mon Sep 17 00:00:00 2001 From: beetrees Date: Sat, 1 Oct 2022 22:12:03 +0100 Subject: [PATCH 0018/1080] Pass 128-bit C-style enum enumerator values to LLVM --- .../src/debuginfo/metadata/enums/cpp_like.rs | 2 +- .../src/debuginfo/metadata/enums/mod.rs | 16 ++++++---------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 ++- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 5 +++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 129e336c7e431..53e8a291d1e8a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -462,7 +462,7 @@ fn build_variant_names_type_di_node<'ll, 'tcx>( cx, "VariantNames", variant_names_enum_base_type(cx), - variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32() as u64)), + variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32().into())), containing_scope, ) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 14044d0f99b98..cb558a50d9148 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -91,9 +91,7 @@ fn build_c_style_enum_di_node<'ll, 'tcx>( tag_base_type(cx, enum_type_and_layout), enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); - // Is there anything we can do to support 128-bit C-Style enums? - let value = discr.val as u64; - (name, value) + (name, discr.val) }), containing_scope, ), @@ -147,14 +145,11 @@ fn tag_base_type<'ll, 'tcx>( /// This is a helper function and does not register anything in the type map by itself. /// /// `variants` is an iterator of (discr-value, variant-name). -/// -// NOTE: Handling of discriminant values is somewhat inconsistent. They can appear as u128, -// u64, and i64. Here everything gets mapped to i64 because that's what LLVM's API expects. fn build_enumeration_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, type_name: &str, base_type: Ty<'tcx>, - enumerators: impl Iterator, u64)>, + enumerators: impl Iterator, u128)>, containing_scope: &'ll DIType, ) -> &'ll DIType { let is_unsigned = match base_type.kind() { @@ -162,21 +157,22 @@ fn build_enumeration_type_di_node<'ll, 'tcx>( ty::Uint(_) => true, _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), }; + let (size, align) = cx.size_and_align_of(base_type); let enumerator_di_nodes: SmallVec> = enumerators .map(|(name, value)| unsafe { + let value = [value as u64, (value >> 64) as u64]; Some(llvm::LLVMRustDIBuilderCreateEnumerator( DIB(cx), name.as_ptr().cast(), name.len(), - value as i64, + value.as_ptr(), + size.bits() as libc::c_uint, is_unsigned, )) }) .collect(); - let (size, align) = cx.size_and_align_of(base_type); - unsafe { llvm::LLVMRustDIBuilderCreateEnumerationType( DIB(cx), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 42cb694c0e75a..3b8a9f3f77f5a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2118,7 +2118,8 @@ extern "C" { Builder: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, - Value: i64, + Value: *const u64, + SizeInBits: c_uint, IsUnsigned: bool, ) -> &'a DIEnumerator; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6f36281af23cc..f5adffd1f06d1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -998,8 +998,9 @@ extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, - int64_t Value, bool IsUnsigned) { - return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); + const uint64_t Value[2], unsigned SizeInBits, bool IsUnsigned) { + return wrap(Builder->createEnumerator(StringRef(Name, NameLen), + APSInt(APInt(SizeInBits, makeArrayRef(Value, 2)), IsUnsigned))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( From 6f6eebde2012dcb163726c66f64fce4b5172dd9f Mon Sep 17 00:00:00 2001 From: beetrees Date: Thu, 6 Oct 2022 15:14:29 +0100 Subject: [PATCH 0019/1080] Add a test for C-style repr128 enum DWARF debuginfo --- src/test/run-make/repr128-dwarf/Makefile | 16 ++++++++++++++++ src/test/run-make/repr128-dwarf/lib.rs | 23 +++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/run-make/repr128-dwarf/Makefile create mode 100644 src/test/run-make/repr128-dwarf/lib.rs diff --git a/src/test/run-make/repr128-dwarf/Makefile b/src/test/run-make/repr128-dwarf/Makefile new file mode 100644 index 0000000000000..a840e3ee6d80b --- /dev/null +++ b/src/test/run-make/repr128-dwarf/Makefile @@ -0,0 +1,16 @@ +# ignore-windows +# This test should be replaced with one in src/test/debuginfo once GDB or LLDB support 128-bit +# enums. + +include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) -Cdebuginfo=2 lib.rs -o $(TMPDIR)/repr128.rlib + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128A $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128B $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128C $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128D $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128A $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128B $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128C $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 )" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128D $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value (<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 7f )" diff --git a/src/test/run-make/repr128-dwarf/lib.rs b/src/test/run-make/repr128-dwarf/lib.rs new file mode 100644 index 0000000000000..63675441d4bab --- /dev/null +++ b/src/test/run-make/repr128-dwarf/lib.rs @@ -0,0 +1,23 @@ +#![crate_type = "lib"] +#![feature(repr128)] + +// Use .to_le() to ensure that the bytes are in the same order on both little- and big-endian +// platforms. + +#[repr(u128)] +pub enum U128Enum { + U128A = 0_u128.to_le(), + U128B = 1_u128.to_le(), + U128C = (u64::MAX as u128 + 1).to_le(), + U128D = u128::MAX.to_le(), +} + +#[repr(i128)] +pub enum I128Enum { + I128A = 0_i128.to_le(), + I128B = (-1_i128).to_le(), + I128C = i128::MIN.to_le(), + I128D = i128::MAX.to_le(), +} + +pub fn f(_: U128Enum, _: I128Enum) {} From 0f46f2773aafc9f9cfe3018b9e896c39913044d5 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 10 Oct 2022 00:47:53 -0400 Subject: [PATCH 0020/1080] Migrate most of `ide_assists::utils` to format arg capture --- crates/ide-assists/src/utils.rs | 16 +++---- .../src/utils/gen_trait_fn_body.rs | 44 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 38396cd7d7baf..acbb56c5f7550 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -189,8 +189,8 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor let mut placeholder = cursor.node().to_string(); escape(&mut placeholder); let tab_stop = match cursor { - Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder), - Cursor::Before(placeholder) => format!("$0{}", placeholder), + Cursor::Replace(placeholder) => format!("${{0:{placeholder}}}"), + Cursor::Before(placeholder) => format!("$0{placeholder}"), }; let mut buf = node.to_string(); @@ -535,17 +535,17 @@ impl ReferenceConversion { ReferenceConversionType::AsRefSlice => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("&[{}]", type_argument_name) + format!("&[{type_argument_name}]") } ReferenceConversionType::Dereferenced => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("&{}", type_argument_name) + format!("&{type_argument_name}") } ReferenceConversionType::Option => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("Option<&{}>", type_argument_name) + format!("Option<&{type_argument_name}>") } ReferenceConversionType::Result => { let mut type_arguments = self.ty.type_arguments(); @@ -553,19 +553,19 @@ impl ReferenceConversion { type_arguments.next().unwrap().display(db).to_string(); let second_type_argument_name = type_arguments.next().unwrap().display(db).to_string(); - format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name) + format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>") } } } pub(crate) fn getter(&self, field_name: String) -> String { match self.conversion { - ReferenceConversionType::Copy => format!("self.{}", field_name), + ReferenceConversionType::Copy => format!("self.{field_name}"), ReferenceConversionType::AsRefStr | ReferenceConversionType::AsRefSlice | ReferenceConversionType::Dereferenced | ReferenceConversionType::Option - | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name), + | ReferenceConversionType::Result => format!("self.{field_name}.as_ref()"), } } } diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 7a0c912959a12..6c87e66c134d7 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -41,7 +41,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?; + let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; match variant.field_list() { // => match self { Self::Name { x } => Self::Name { x: x.clone() } } @@ -70,7 +70,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut pats = vec![]; let mut fields = vec![]; for (i, _) in list.fields().enumerate() { - let field_name = format!("arg{}", i); + let field_name = format!("arg{i}"); let pat = make::ident_pat(false, false, make::name(&field_name)); pats.push(pat.into()); @@ -118,7 +118,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut fields = vec![]; for (i, _) in field_list.fields().enumerate() { let f_path = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(f_path, &format!("{}", i)); + let target = make::expr_field(f_path, &format!("{i}")); fields.push(gen_clone_call(target)); } let struct_name = make::expr_path(make::ext::ident_path("Self")); @@ -151,7 +151,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?; + let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; let target = make::expr_path(make::ext::ident_path("f")); match variant.field_list() { @@ -159,7 +159,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => f.debug_struct(name) let target = make::expr_path(make::ext::ident_path("f")); let method = make::name_ref("debug_struct"); - let struct_name = format!("\"{}\"", name); + let struct_name = format!("\"{name}\""); let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); let mut expr = make::expr_method_call(target, method, args); @@ -173,8 +173,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => .field("field_name", field) let method_name = make::name_ref("field"); - let name = make::expr_literal(&(format!("\"{}\"", field_name))).into(); - let path = &format!("{}", field_name); + let name = make::expr_literal(&(format!("\"{field_name}\""))).into(); + let path = &format!("{field_name}"); let path = make::expr_path(make::ext::ident_path(path)); let args = make::arg_list(vec![name, path]); expr = make::expr_method_call(expr, method_name, args); @@ -192,13 +192,13 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => f.debug_tuple(name) let target = make::expr_path(make::ext::ident_path("f")); let method = make::name_ref("debug_tuple"); - let struct_name = format!("\"{}\"", name); + let struct_name = format!("\"{name}\""); let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); let mut expr = make::expr_method_call(target, method, args); let mut pats = vec![]; for (i, _) in list.fields().enumerate() { - let name = format!("arg{}", i); + let name = format!("arg{i}"); // create a field pattern for use in `MyStruct(fields..)` let field_name = make::name(&name); @@ -222,7 +222,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { arms.push(make::match_arm(Some(pat.into()), None, expr)); } None => { - let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into(); + let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into(); let args = make::arg_list([target, fmt_string]); let macro_name = make::expr_path(make::ext::ident_path("write")); let macro_call = make::expr_macro_call(macro_name, args); @@ -244,7 +244,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } ast::Adt::Struct(strukt) => { - let name = format!("\"{}\"", annotated_name); + let name = format!("\"{annotated_name}\""); let args = make::arg_list(Some(make::expr_literal(&name).into())); let target = make::expr_path(make::ext::ident_path("f")); @@ -258,10 +258,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut expr = make::expr_method_call(target, method, args); for field in field_list.fields() { let name = field.name()?; - let f_name = make::expr_literal(&(format!("\"{}\"", name))).into(); + let f_name = make::expr_literal(&(format!("\"{name}\""))).into(); let f_path = make::expr_path(make::ext::ident_path("self")); let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{}", name)); + let f_path = make::expr_field(f_path, &format!("{name}")); let args = make::arg_list([f_name, f_path]); expr = make::expr_method_call(expr, make::name_ref("field"), args); } @@ -275,7 +275,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { for (i, _) in field_list.fields().enumerate() { let f_path = make::expr_path(make::ext::ident_path("self")); let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{}", i)); + let f_path = make::expr_field(f_path, &format!("{i}")); let method = make::name_ref("field"); expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path))); } @@ -379,7 +379,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut stmts = vec![]; for (i, _) in field_list.fields().enumerate() { let base = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(base, &format!("{}", i)); + let target = make::expr_field(base, &format!("{i}")); stmts.push(gen_hash_call(target)); } make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) @@ -453,10 +453,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { for field in list.fields() { let field_name = field.name()?.to_string(); - let l_name = &format!("l_{}", field_name); + let l_name = &format!("l_{field_name}"); l_fields.push(gen_record_pat_field(&field_name, l_name)); - let r_name = &format!("r_{}", field_name); + let r_name = &format!("r_{field_name}"); r_fields.push(gen_record_pat_field(&field_name, r_name)); let lhs = make::expr_path(make::ext::ident_path(l_name)); @@ -484,12 +484,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut r_fields = vec![]; for (i, _) in list.fields().enumerate() { - let field_name = format!("{}", i); + let field_name = format!("{i}"); - let l_name = format!("l{}", field_name); + let l_name = format!("l{field_name}"); l_fields.push(gen_tuple_field(&l_name)); - let r_name = format!("r{}", field_name); + let r_name = format!("r{field_name}"); r_fields.push(gen_tuple_field(&r_name)); let lhs = make::expr_path(make::ext::ident_path(&l_name)); @@ -548,7 +548,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(ast::FieldList::TupleFieldList(field_list)) => { let mut expr = None; for (i, _) in field_list.fields().enumerate() { - let idx = format!("{}", i); + let idx = format!("{i}"); let lhs = make::expr_path(make::ext::ident_path("self")); let lhs = make::expr_field(lhs, &idx); let rhs = make::expr_path(make::ext::ident_path("other")); @@ -628,7 +628,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(ast::FieldList::TupleFieldList(field_list)) => { let mut exprs = vec![]; for (i, _) in field_list.fields().enumerate() { - let idx = format!("{}", i); + let idx = format!("{i}"); let lhs = make::expr_path(make::ext::ident_path("self")); let lhs = make::expr_field(lhs, &idx); let rhs = make::expr_path(make::ext::ident_path("other")); From d439fb2bc89581aef419114dffde693b44bf088f Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 10 Oct 2022 11:04:38 -0400 Subject: [PATCH 0021/1080] Migrate assists to format args captures, part 1 --- .../src/handlers/add_explicit_type.rs | 4 +- .../src/handlers/add_return_type.rs | 6 +-- .../src/handlers/add_turbo_fish.rs | 7 +-- .../src/handlers/apply_demorgan.rs | 6 +-- .../ide-assists/src/handlers/auto_import.rs | 6 ++- .../src/handlers/convert_comment_block.rs | 13 ++--- .../src/handlers/convert_integer_literal.rs | 10 ++-- .../src/handlers/convert_into_to_from.rs | 4 +- .../handlers/convert_iter_for_each_to_for.rs | 12 ++--- .../src/handlers/convert_let_else_to_match.rs | 6 +-- .../convert_tuple_struct_to_named_struct.rs | 8 +++- ...ert_two_arm_bool_match_to_matches_macro.rs | 10 ++-- .../src/handlers/destructure_tuple_binding.rs | 12 ++--- .../src/handlers/extract_function.rs | 48 ++++++++----------- .../src/handlers/extract_module.rs | 31 ++++++------ .../extract_struct_from_enum_variant.rs | 10 ++-- .../src/handlers/extract_type_alias.rs | 38 +++++---------- .../src/handlers/extract_variable.rs | 14 +++--- .../src/handlers/fix_visibility.rs | 18 +++---- 19 files changed, 128 insertions(+), 135 deletions(-) diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs index bfa9759ec84be..b5f99726fe1c8 100644 --- a/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -69,14 +69,14 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; acc.add( AssistId("add_explicit_type", AssistKind::RefactorRewrite), - format!("Insert explicit type `{}`", inferred_type), + format!("Insert explicit type `{inferred_type}`"), pat_range, |builder| match ascribed_ty { Some(ascribed_ty) => { builder.replace(ascribed_ty.syntax().text_range(), inferred_type); } None => { - builder.insert(pat_range.end(), format!(": {}", inferred_type)); + builder.insert(pat_range.end(), format!(": {inferred_type}")); } }, ) diff --git a/crates/ide-assists/src/handlers/add_return_type.rs b/crates/ide-assists/src/handlers/add_return_type.rs index f858d7a15c242..89040a8569e63 100644 --- a/crates/ide-assists/src/handlers/add_return_type.rs +++ b/crates/ide-assists/src/handlers/add_return_type.rs @@ -35,16 +35,16 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt match builder_edit_pos { InsertOrReplace::Insert(insert_pos, needs_whitespace) => { let preceeding_whitespace = if needs_whitespace { " " } else { "" }; - builder.insert(insert_pos, &format!("{}-> {} ", preceeding_whitespace, ty)) + builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} ")) } InsertOrReplace::Replace(text_range) => { - builder.replace(text_range, &format!("-> {}", ty)) + builder.replace(text_range, &format!("-> {ty}")) } } if let FnType::Closure { wrap_expr: true } = fn_type { cov_mark::hit!(wrap_closure_non_block_expr); // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block - builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); + builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}")); } }, ) diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index c0bf238db7317..acf82e4b25794 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -93,12 +93,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.trigger_signature_help(); match ctx.config.snippet_cap { Some(cap) => { - let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments)); + let fish_head = get_snippet_fish_head(number_of_arguments); + let snip = format!("::<{fish_head}>"); builder.insert_snippet(cap, ident.text_range().end(), snip) } None => { let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", "); - let snip = format!("::<{}>", fish_head); + let snip = format!("::<{fish_head}>"); builder.insert(ident.text_range().end(), snip); } } @@ -109,7 +110,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti /// This will create a snippet string with tabstops marked fn get_snippet_fish_head(number_of_arguments: usize) -> String { let mut fish_head = (1..number_of_arguments) - .format_with("", |i, f| f(&format_args!("${{{}:_}}, ", i))) + .format_with("", |i, f| f(&format_args!("${{{i}:_}}, "))) .to_string(); // tabstop 0 is a special case and always the last one diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 2853d1d1be3cd..57cfa17cc8e13 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -123,20 +123,20 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let lhs_range = lhs.syntax().text_range(); let not_lhs = invert_boolean_expression(lhs); - edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); + edit.replace(lhs_range, format!("!({not_lhs}")); } if let Some(rhs) = terms.pop_back() { let rhs_range = rhs.syntax().text_range(); let not_rhs = invert_boolean_expression(rhs); - edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); + edit.replace(rhs_range, format!("{not_rhs})")); } for term in terms { let term_range = term.syntax().text_range(); let not_term = invert_boolean_expression(term); - edit.replace(term_range, not_term.syntax().text()); + edit.replace(term_range, not_term.to_string()); } } }, diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index e257218ba9376..b6584f729a40e 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -127,10 +127,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref()))); for import in proposed_imports { + let import_path = import.import_path; + acc.add_group( &group_label, AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{}`", import.import_path), + format!("Import `{import_path}`"), range, |builder| { let scope = match scope.clone() { @@ -138,7 +140,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), }; - insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use); + insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use); }, ); } diff --git a/crates/ide-assists/src/handlers/convert_comment_block.rs b/crates/ide-assists/src/handlers/convert_comment_block.rs index f171dd81a811e..312cb65abd2a1 100644 --- a/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -54,16 +54,17 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let indent_spaces = indentation.to_string(); let output = lines - .map(|l| l.trim_start_matches(&indent_spaces)) - .map(|l| { + .map(|line| { + let line = line.trim_start_matches(&indent_spaces); + // Don't introduce trailing whitespace - if l.is_empty() { + if line.is_empty() { line_prefix.to_string() } else { - format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces)) + format!("{line_prefix} {line}") } }) - .join(&format!("\n{}", indent_spaces)); + .join(&format!("\n{indent_spaces}")); edit.replace(target, output) }, @@ -96,7 +97,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let block_prefix = CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix(); - let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation); + let output = format!("{block_prefix}\n{block_comment_body}\n{indentation}*/"); edit.replace(target, output) }, diff --git a/crates/ide-assists/src/handlers/convert_integer_literal.rs b/crates/ide-assists/src/handlers/convert_integer_literal.rs index 9060696cdc8e9..ff2195f7e6c4a 100644 --- a/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -32,19 +32,19 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_> } let mut converted = match target_radix { - Radix::Binary => format!("0b{:b}", value), - Radix::Octal => format!("0o{:o}", value), + Radix::Binary => format!("0b{value:b}"), + Radix::Octal => format!("0o{value:o}"), Radix::Decimal => value.to_string(), - Radix::Hexadecimal => format!("0x{:X}", value), + Radix::Hexadecimal => format!("0x{value:X}"), }; - let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default()); - // Appends the type suffix back into the new literal if it exists. if let Some(suffix) = suffix { converted.push_str(suffix); } + let label = format!("Convert {literal} to {converted}"); + acc.add_group( &group_id, AssistId("convert_integer_literal", AssistKind::RefactorInline), diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index 95d11abe8bc0f..872b52c98fff2 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -86,9 +86,9 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - impl_.syntax().text_range(), |builder| { builder.replace(src_type.syntax().text_range(), dest_type.to_string()); - builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type)); + builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>")); builder.replace(into_fn_return.syntax().text_range(), "-> Self"); - builder.replace(into_fn_params.syntax().text_range(), format!("(val: {})", src_type)); + builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})")); builder.replace(into_fn_name.syntax().text_range(), "from"); for s in selfs { diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 2cf370c090743..80eecf4a09868 100644 --- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -119,19 +119,19 @@ pub(crate) fn convert_for_loop_with_for_each( { // We have either "for x in &col" and col implements a method called iter // or "for x in &mut col" and col implements a method called iter_mut - format_to!(buf, "{}.{}()", expr_behind_ref, method); + format_to!(buf, "{expr_behind_ref}.{method}()"); } else if let ast::Expr::RangeExpr(..) = iterable { // range expressions need to be parenthesized for the syntax to be correct - format_to!(buf, "({})", iterable); + format_to!(buf, "({iterable})"); } else if impls_core_iter(&ctx.sema, &iterable) { - format_to!(buf, "{}", iterable); + format_to!(buf, "{iterable}"); } else if let ast::Expr::RefExpr(_) = iterable { - format_to!(buf, "({}).into_iter()", iterable); + format_to!(buf, "({iterable}).into_iter()"); } else { - format_to!(buf, "{}.into_iter()", iterable); + format_to!(buf, "{iterable}.into_iter()"); } - format_to!(buf, ".for_each(|{}| {});", pat, body); + format_to!(buf, ".for_each(|{pat}| {body});"); builder.replace(for_loop.syntax().text_range(), buf) }, diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 00095de257d5a..c82a3b5303259 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -80,7 +80,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String { .map( |(ident, ismut)| { if *ismut && addmut { - format!("mut {}", ident) + format!("mut {ident}") } else { ident.to_string() } @@ -93,7 +93,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String { } else if binders.len() == 1 { vars } else { - format!("({})", vars) + format!("({vars})") } } @@ -153,7 +153,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' let only_expr = let_else_block.statements().next().is_none(); let branch2 = match &let_else_block.tail_expr() { - Some(tail) if only_expr => format!("{},", tail.syntax().text()), + Some(tail) if only_expr => format!("{tail},"), _ => let_else_block.syntax().text().to_string(), }; let replace = if binders.is_empty() { diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index d8f522708460e..92e091fca126c 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -226,7 +226,13 @@ fn edit_field_references( } fn generate_names(fields: impl Iterator) -> Vec { - fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect() + fields + .enumerate() + .map(|(i, _)| { + let idx = i + 1; + ast::make::name(&format!("field{idx}")) + }) + .collect() } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index 54a7f480a4e46..b1b0f587cd33d 100644 --- a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs @@ -58,16 +58,16 @@ pub(crate) fn convert_two_arm_bool_match_to_matches_macro( target_range, |builder| { let mut arm_str = String::new(); - if let Some(ref pat) = first_arm.pat() { + if let Some(pat) = &first_arm.pat() { arm_str += &pat.to_string(); } - if let Some(ref guard) = first_arm.guard() { - arm_str += &format!(" {}", &guard.to_string()); + if let Some(guard) = &first_arm.guard() { + arm_str += &format!(" {guard}"); } if invert_matches { - builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str)); + builder.replace(target_range, format!("!matches!({expr}, {arm_str})")); } else { - builder.replace(target_range, format!("matches!({}, {})", expr, arm_str)); + builder.replace(target_range, format!("matches!({expr}, {arm_str})")); } }, ) diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index dc581ff3bd2c7..31c2ce7c1b54a 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -133,7 +133,7 @@ fn generate_name( _usages: &Option, ) -> String { // FIXME: detect if name already used - format!("_{}", index) + format!("_{index}") } enum RefType { @@ -168,12 +168,12 @@ fn edit_tuple_assignment( let add_cursor = |text: &str| { // place cursor on first tuple item let first_tuple = &data.field_names[0]; - text.replacen(first_tuple, &format!("$0{}", first_tuple), 1) + text.replacen(first_tuple, &format!("$0{first_tuple}"), 1) }; // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if in_sub_pattern { - let text = format!(" @ {}", tuple_pat); + let text = format!(" @ {tuple_pat}"); match ctx.config.snippet_cap { Some(cap) => { let snip = add_cursor(&text); @@ -314,9 +314,9 @@ struct RefData { impl RefData { fn format(&self, field_name: &str) -> String { match (self.needs_deref, self.needs_parentheses) { - (true, true) => format!("(*{})", field_name), - (true, false) => format!("*{}", field_name), - (false, true) => format!("({})", field_name), + (true, true) => format!("(*{field_name})"), + (true, false) => format!("*{field_name}"), + (false, true) => format!("({field_name})"), (false, false) => field_name.to_string(), } } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index d6c8ea785f84a..0605883584922 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -181,7 +181,7 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef let mut counter = 0; while names_in_scope.contains(&name) { counter += 1; - name = format!("{}{}", &default_name, counter) + name = format!("{default_name}{counter}") } make::name_ref(&name) } @@ -1291,19 +1291,23 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St match fun.outliving_locals.as_slice() { [] => {} [var] => { - format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db())) + let modifier = mut_modifier(var); + let name = var.local.name(ctx.db()); + format_to!(buf, "let {modifier}{name} = ") } vars => { buf.push_str("let ("); let bindings = vars.iter().format_with(", ", |local, f| { - f(&format_args!("{}{}", mut_modifier(local), local.local.name(ctx.db()))) + let modifier = mut_modifier(local); + let name = local.local.name(ctx.db()); + f(&format_args!("{modifier}{name}")) }); - format_to!(buf, "{}", bindings); + format_to!(buf, "{bindings}"); buf.push_str(") = "); } } - format_to!(buf, "{}", expr); + format_to!(buf, "{expr}"); let insert_comma = fun .body .parent() @@ -1447,6 +1451,8 @@ fn format_function( new_indent: IndentLevel, ) -> String { let mut fn_def = String::new(); + + let fun_name = &fun.name; let params = fun.make_param_list(ctx, module); let ret_ty = fun.make_ret_ty(ctx, module); let body = make_body(ctx, old_indent, new_indent, fun); @@ -1454,42 +1460,28 @@ fn format_function( let async_kw = if fun.control_flow.is_async { "async " } else { "" }; let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" }; let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); + + format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}"); match ctx.config.snippet_cap { - Some(_) => format_to!( - fn_def, - "\n\n{}{}{}{}fn $0{}", - new_indent, - const_kw, - async_kw, - unsafe_kw, - fun.name, - ), - None => format_to!( - fn_def, - "\n\n{}{}{}{}fn {}", - new_indent, - const_kw, - async_kw, - unsafe_kw, - fun.name, - ), + Some(_) => format_to!(fn_def, "fn $0{fun_name}"), + None => format_to!(fn_def, "fn {fun_name}"), } if let Some(generic_params) = generic_params { - format_to!(fn_def, "{}", generic_params); + format_to!(fn_def, "{generic_params}"); } - format_to!(fn_def, "{}", params); + format_to!(fn_def, "{params}"); if let Some(ret_ty) = ret_ty { - format_to!(fn_def, " {}", ret_ty); + format_to!(fn_def, " {ret_ty}"); } if let Some(where_clause) = where_clause { - format_to!(fn_def, " {}", where_clause); + format_to!(fn_def, " {where_clause}"); } - format_to!(fn_def, " {}", body); + format_to!(fn_def, " {body}"); fn_def } diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index 897980c665049..56834394aebaa 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -127,7 +127,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti for item in items_to_be_processed { let item = item.indent(IndentLevel(1)); let mut indented_item = String::new(); - format_to!(indented_item, "{}{}", new_item_indent, item.to_string()); + format_to!(indented_item, "{new_item_indent}{item}"); body_items.push(indented_item); } @@ -137,30 +137,28 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let mut impl_body_def = String::new(); if let Some(self_ty) = impl_.self_ty() { - format_to!( - impl_body_def, - "{}impl {} {{\n{}\n{}}}", - old_item_indent + 1, - self_ty.to_string(), - body, - old_item_indent + 1 - ); - + { + let impl_indent = old_item_indent + 1; + format_to!( + impl_body_def, + "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}", + ); + } body = impl_body_def; // Add the import for enum/struct corresponding to given impl block module.make_use_stmt_of_node_with_super(self_ty.syntax()); for item in module.use_items { - let mut indented_item = String::new(); - format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string()); - body = format!("{}\n\n{}", indented_item, body); + let item_indent = old_item_indent + 1; + body = format!("{item_indent}{item}\n\n{body}"); } } } let mut module_def = String::new(); - format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent); + let module_name = module.name; + format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}"); let mut usages_to_be_updated_for_curr_file = vec![]; for usages_to_be_updated_for_file in usages_to_be_processed { @@ -199,7 +197,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.delete(range); } - builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def)); + builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { builder.replace(module.text_range, module_def) } @@ -343,9 +341,10 @@ impl Module { && !self.text_range.contains_range(desc.text_range()) { if let Some(name_ref) = ast::NameRef::cast(desc) { + let mod_name = self.name; return Some(( name_ref.syntax().text_range(), - format!("{}::{}", self.name, name_ref), + format!("{mod_name}::{name_ref}"), )); } } diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 970e948dfd930..b4e10667b07ab 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -296,10 +296,14 @@ fn create_struct_def( fn update_variant(variant: &ast::Variant, generics: Option) -> Option<()> { let name = variant.name()?; - let ty = generics + let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) - .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args()))) - .unwrap_or_else(|| make::ty(&name.text())); + .map(|generics| generics.to_generic_args()); + // FIXME: replace with a `ast::make` constructor + let ty = match generic_args { + Some(generic_args) => make::ty(&format!("{name}{generic_args}")), + None => make::ty(&name.text()), + }; // change from a record to a tuple field list let tuple_field = make::tuple_field(None, ty); diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index 03aa8601d14e1..3116935fc5e75 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -1,8 +1,7 @@ use either::Either; use ide_db::syntax_helpers::node_ext::walk_ty; -use itertools::Itertools; use syntax::{ - ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName}, + ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName}, match_ast, }; @@ -64,41 +63,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> known_generics.extend(it.generic_params()); } let generics = collect_used_generics(&ty, &known_generics); + let generic_params = + generics.map(|it| make::generic_param_list(it.into_iter().cloned())); - let replacement = if !generics.is_empty() { - format!( - "Type<{}>", - generics.iter().format_with(", ", |generic, f| { - match generic { - ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()), - ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()), - ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()), - } - }) - ) - } else { - String::from("Type") - }; + let ty_args = generic_params + .as_ref() + .map_or(String::new(), |it| it.to_generic_args().to_string()); + let replacement = format!("Type{ty_args}"); builder.replace(target, replacement); let indent = IndentLevel::from_node(node); - let generics = if !generics.is_empty() { - format!("<{}>", generics.iter().format(", ")) - } else { - String::new() - }; + let generic_params = generic_params.map_or(String::new(), |it| it.to_string()); match ctx.config.snippet_cap { Some(cap) => { builder.insert_snippet( cap, insert_pos, - format!("type $0Type{} = {};\n\n{}", generics, ty, indent), + format!("type $0Type{generic_params} = {ty};\n\n{indent}"), ); } None => { builder.insert( insert_pos, - format!("type Type{} = {};\n\n{}", generics, ty, indent), + format!("type Type{generic_params} = {ty};\n\n{indent}"), ); } } @@ -109,7 +96,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> fn collect_used_generics<'gp>( ty: &ast::Type, known_generics: &'gp [ast::GenericParam], -) -> Vec<&'gp ast::GenericParam> { +) -> Option> { // can't use a closure -> closure here cause lifetime inference fails for that fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ { move |gp: &&ast::GenericParam| match gp { @@ -198,7 +185,8 @@ fn collect_used_generics<'gp>( ast::GenericParam::LifetimeParam(_) => 0, ast::GenericParam::TypeParam(_) => 1, }); - generics + + Some(generics).filter(|it| it.len() > 0) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 3596b6f82381b..a738deffb95b3 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -91,13 +91,13 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match anchor { Anchor::Before(_) | Anchor::Replace(_) => { - format_to!(buf, "let {}{} = {}", var_modifier, var_name, reference_modifier) + format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}") } Anchor::WrapInBlock(_) => { - format_to!(buf, "{{ let {} = {}", var_name, reference_modifier) + format_to!(buf, "{{ let {var_name} = {reference_modifier}") } }; - format_to!(buf, "{}", to_extract.syntax()); + format_to!(buf, "{to_extract}"); if let Anchor::Replace(stmt) = anchor { cov_mark::hit!(test_extract_var_expr_stmt); @@ -107,8 +107,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match ctx.config.snippet_cap { Some(cap) => { let snip = buf.replace( - &format!("let {}{}", var_modifier, var_name), - &format!("let {}$0{}", var_modifier, var_name), + &format!("let {var_modifier}{var_name}"), + &format!("let {var_modifier}$0{var_name}"), ); edit.replace_snippet(cap, expr_range, snip) } @@ -135,8 +135,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match ctx.config.snippet_cap { Some(cap) => { let snip = buf.replace( - &format!("let {}{}", var_modifier, var_name), - &format!("let {}$0{}", var_modifier, var_name), + &format!("let {var_modifier}{var_name}"), + &format!("let {var_modifier}$0{var_name}"), ); edit.insert_snippet(cap, offset, snip) } diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index b33846f546653..8764543028706 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -57,8 +57,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; let assist_label = match target_name { - None => format!("Change visibility to {}", missing_visibility), - Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), + None => format!("Change visibility to {missing_visibility}"), + Some(name) => format!("Change visibility of {name} to {missing_visibility}"), }; acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { @@ -68,15 +68,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) Some(current_visibility) => builder.replace_snippet( cap, current_visibility.syntax().text_range(), - format!("$0{}", missing_visibility), + format!("$0{missing_visibility}"), ), - None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), }, None => match current_visibility { Some(current_visibility) => { builder.replace(current_visibility.syntax().text_range(), missing_visibility) } - None => builder.insert(offset, format!("{} ", missing_visibility)), + None => builder.insert(offset, format!("{missing_visibility} ")), }, } }) @@ -114,7 +114,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> let target_name = record_field_def.name(ctx.db()); let assist_label = - format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); + format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}"); acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { builder.edit_file(target_file); @@ -123,15 +123,15 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> Some(current_visibility) => builder.replace_snippet( cap, current_visibility.syntax().text_range(), - format!("$0{}", missing_visibility), + format!("$0{missing_visibility}"), ), - None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), }, None => match current_visibility { Some(current_visibility) => { builder.replace(current_visibility.syntax().text_range(), missing_visibility) } - None => builder.insert(offset, format!("{} ", missing_visibility)), + None => builder.insert(offset, format!("{missing_visibility} ")), }, } }) From 0c158f0e9d708e7705249bca8e38757ebf2e5c8c Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 10 Oct 2022 16:00:46 -0700 Subject: [PATCH 0022/1080] Add a test case for #[track_caller] on async fn --- .../ui/async-await/panic-no-track-caller.rs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/test/ui/async-await/panic-no-track-caller.rs diff --git a/src/test/ui/async-await/panic-no-track-caller.rs b/src/test/ui/async-await/panic-no-track-caller.rs new file mode 100644 index 0000000000000..934764912d35a --- /dev/null +++ b/src/test/ui/async-await/panic-no-track-caller.rs @@ -0,0 +1,74 @@ +// run-pass +// edition:2021 + +use std::future::Future; +use std::panic; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Wake}; +use std::thread::{self, Thread}; + +/// A waker that wakes up the current thread when called. +struct ThreadWaker(Thread); + +impl Wake for ThreadWaker { + fn wake(self: Arc) { + self.0.unpark(); + } +} + +/// Run a future to completion on the current thread. +fn block_on(fut: impl Future) -> T { + // Pin the future so it can be polled. + let mut fut = Box::pin(fut); + + // Create a new context to be passed to the future. + let t = thread::current(); + let waker = Arc::new(ThreadWaker(t)).into(); + let mut cx = Context::from_waker(&waker); + + // Run the future to completion. + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(res) => return res, + Poll::Pending => thread::park(), + } + } +} + +async fn bar() { + panic!() +} + +async fn foo() { + bar().await +} + +#[track_caller] +async fn bar_track_caller() { + panic!() +} + +async fn foo_track_caller() { + bar_track_caller().await +} + +fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { + let loc = Arc::new(Mutex::new(None)); + + let hook = panic::take_hook(); + { + let loc = loc.clone(); + panic::set_hook(Box::new(move |info| { + *loc.lock().unwrap() = info.location().map(|loc| loc.line()) + })); + } + panic::catch_unwind(f).unwrap_err(); + panic::set_hook(hook); + let x = loc.lock().unwrap().unwrap(); + x +} + +fn main() { + assert_eq!(panicked_at(|| block_on(foo())), 39); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 52); +} From 9ebd6916125e9181d1ce340a159e9403afc99188 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 10 Oct 2022 23:37:09 -0500 Subject: [PATCH 0023/1080] Fix allow_attributes_without_reason applying to external crate macros Previously the `clippy::allow_attributes_without_reason` lint would apply to external crate macros. Many macros in the Rust ecosystem include these `allow` attributes without adding a reason, making this lint pretty much unusable in any sizable Rust project. This commit fixes that by adding a check to the lint if the attribute is from an external crate macro and returning early. --- clippy_lints/src/attrs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e8f..bed9cf565f30b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -464,6 +464,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, From 848744403a67965f792d9f0eacb7700d8e325c2f Mon Sep 17 00:00:00 2001 From: Andrew Tribick Date: Tue, 11 Oct 2022 23:09:23 +0200 Subject: [PATCH 0024/1080] Fix inconsistent rounding of 0.5 when formatted to 0 decimal places --- library/core/src/num/flt2dec/strategy/dragon.rs | 2 +- library/core/tests/fmt/float.rs | 4 ++-- library/core/tests/num/flt2dec/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index 8ced5971ec2f9..71b14d0ae3f4c 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -366,7 +366,7 @@ pub fn format_exact<'a>( if order == Ordering::Greater || (order == Ordering::Equal // SAFETY: `buf[len-1]` is initialized. - && (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1)) + && len > 0 && unsafe { buf[len - 1].assume_init() } & 1 == 1) { // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... diff --git a/library/core/tests/fmt/float.rs b/library/core/tests/fmt/float.rs index 47a7400f76ef9..bc0bef6d77604 100644 --- a/library/core/tests/fmt/float.rs +++ b/library/core/tests/fmt/float.rs @@ -5,7 +5,7 @@ fn test_format_f64() { assert_eq!("10", format!("{:.0}", 9.9f64)); assert_eq!("9.8", format!("{:.1}", 9.849f64)); assert_eq!("9.9", format!("{:.1}", 9.851f64)); - assert_eq!("1", format!("{:.0}", 0.5f64)); + assert_eq!("0", format!("{:.0}", 0.5f64)); assert_eq!("1.23456789e6", format!("{:e}", 1234567.89f64)); assert_eq!("1.23456789e3", format!("{:e}", 1234.56789f64)); assert_eq!("1.23456789E6", format!("{:E}", 1234567.89f64)); @@ -31,7 +31,7 @@ fn test_format_f32() { assert_eq!("10", format!("{:.0}", 9.9f32)); assert_eq!("9.8", format!("{:.1}", 9.849f32)); assert_eq!("9.9", format!("{:.1}", 9.851f32)); - assert_eq!("1", format!("{:.0}", 0.5f32)); + assert_eq!("0", format!("{:.0}", 0.5f32)); assert_eq!("1.2345679e6", format!("{:e}", 1234567.89f32)); assert_eq!("1.2345679e3", format!("{:e}", 1234.56789f32)); assert_eq!("1.2345679E6", format!("{:E}", 1234567.89f32)); diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 798473bbde377..30843cc3dd79c 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -138,7 +138,7 @@ where // check exact rounding for zero- and negative-width cases let start; - if expected[0] >= b'5' { + if expected[0] > b'5' { try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; "zero-width rounding-up mismatch for v={v}: \ actual {actual:?}, expected {expected:?}", @@ -1007,7 +1007,7 @@ where assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); - assert_eq!(to_string(f, 0.5, Minus, 0), "1"); + assert_eq!(to_string(f, 0.5, Minus, 0), "0"); assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); From 3db41d13f08db377c9bc516d8f285f61ed668edd Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 11 Oct 2022 16:37:54 -0700 Subject: [PATCH 0025/1080] wip: trying to enable #[track_caller] on async fn --- compiler/rustc_ast_lowering/src/expr.rs | 20 ++++++++++++++++++- compiler/rustc_ast_lowering/src/item.rs | 2 +- ...-track-caller.rs => panic-track-caller.rs} | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) rename src/test/ui/async-await/{panic-no-track-caller.rs => panic-track-caller.rs} (98%) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 46886c518afd5..61d91874e6173 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -617,8 +617,26 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Closure(c) }; + let generator_hir_id = self.lower_node_id(closure_node_id); + // FIXME: only add track caller if the parent is track_caller + self.lower_attrs( + generator_hir_id, + &[Attribute { + kind: AttrKind::Normal(ptr::P(NormalAttr { + item: AttrItem { + path: Path::from_ident(Ident::new(sym::track_caller, span)), + args: MacArgs::Empty, + tokens: None, + }, + tokens: None, + })), + id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(), + style: AttrStyle::Outer, + span, + }], + ); let generator = hir::Expr { - hir_id: self.lower_node_id(closure_node_id), + hir_id: generator_hir_id, kind: generator_kind, span: self.lower_span(span), }; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 9a46444d82398..e9cc53b7138c9 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -86,7 +86,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()), - allow_gen_future: Some([sym::gen_future][..].into()), + allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()), allow_into_future: Some([sym::into_future][..].into()), generics_def_id_map: Default::default(), }; diff --git a/src/test/ui/async-await/panic-no-track-caller.rs b/src/test/ui/async-await/panic-track-caller.rs similarity index 98% rename from src/test/ui/async-await/panic-no-track-caller.rs rename to src/test/ui/async-await/panic-track-caller.rs index 934764912d35a..76776d41c57c7 100644 --- a/src/test/ui/async-await/panic-no-track-caller.rs +++ b/src/test/ui/async-await/panic-track-caller.rs @@ -1,5 +1,6 @@ // run-pass // edition:2021 +#![feature(closure_track_caller)] use std::future::Future; use std::panic; From b1909a80af489974edb625dbd2c481b2001ecb53 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 10 Oct 2022 14:20:27 -0400 Subject: [PATCH 0026/1080] Migrate assists to format args captures, part 3 --- .../ide-assists/src/handlers/inline_call.rs | 4 +-- .../src/handlers/inline_local_variable.rs | 4 +-- .../src/handlers/introduce_named_lifetime.rs | 2 +- .../src/handlers/merge_match_arms.rs | 2 +- .../src/handlers/move_from_mod_rs.rs | 4 +-- crates/ide-assists/src/handlers/move_guard.rs | 10 +++---- .../src/handlers/move_module_to_file.rs | 4 +-- .../src/handlers/move_to_mod_rs.rs | 4 +-- .../src/handlers/number_representation.rs | 2 +- .../src/handlers/qualify_method_call.rs | 2 +- .../ide-assists/src/handlers/qualify_path.rs | 29 +++++++++---------- crates/ide-assists/src/handlers/raw_string.rs | 9 ++---- crates/ide-assists/src/handlers/remove_dbg.rs | 6 ++-- .../replace_derive_with_manual_impl.rs | 9 ++---- .../src/handlers/replace_or_with_or_else.rs | 4 +-- .../replace_turbofish_with_explicit_type.rs | 2 +- .../ide-assists/src/handlers/unwrap_tuple.rs | 4 +-- .../handlers/wrap_return_type_in_result.rs | 4 +-- 18 files changed, 48 insertions(+), 57 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 9f51cdaf8b1eb..0d1acacab5067 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -190,10 +190,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< PathResolution::Def(hir::ModuleDef::Function(f)) => f, _ => return None, }; - (function, format!("Inline `{}`", path)) + (function, format!("Inline `{path}`")) } ast::CallableExpr::MethodCall(call) => { - (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref)) + (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`")) } }; diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 7259d67819416..ce44100e34beb 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -113,7 +113,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) .collect::>>()?; let init_str = initializer_expr.syntax().text().to_string(); - let init_in_paren = format!("({})", &init_str); + let init_in_paren = format!("({init_str})"); let target = match target { ast::NameOrNameRef::Name(it) => it.syntax().text_range(), @@ -132,7 +132,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) let replacement = if should_wrap { &init_in_paren } else { &init_str }; if ast::RecordExprField::for_field_name(&name).is_some() { cov_mark::hit!(inline_field_shorthand); - builder.insert(range.end(), format!(": {}", replacement)); + builder.insert(range.end(), format!(": {replacement}")); } else { builder.replace(range, replacement.clone()) } diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 2fc754e3e50d1..a54dc4f96de00 100644 --- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -127,7 +127,7 @@ fn generate_unique_lifetime_param_name( Some(type_params) => { let used_lifetime_params: FxHashSet<_> = type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); - ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it)) + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) } None => Some("'a".to_string()), } diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs index c24015b1c5175..641c90885bf53 100644 --- a/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -78,7 +78,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .join(" | ") }; - let arm = format!("{} => {},", pats, current_expr.syntax().text()); + let arm = format!("{pats} => {current_expr},"); if let [first, .., last] = &*arms_to_merge { let start = first.syntax().text_range().start(); diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs index a6c85a2b18b34..1728c03cd03e2 100644 --- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -40,11 +40,11 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let target = source_file.syntax().text_range(); let module_name = module.name(ctx.db())?.to_string(); - let path = format!("../{}.rs", module_name); + let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( AssistId("move_from_mod_rs", AssistKind::Refactor), - format!("Convert {}/mod.rs to {}.rs", module_name, module_name), + format!("Convert {module_name}/mod.rs to {module_name}.rs"), target, |builder| { builder.move_file(ctx.file_id(), dst); diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs index b8f1b36deb93c..ec3281619cc3f 100644 --- a/crates/ide-assists/src/handlers/move_guard.rs +++ b/crates/ide-assists/src/handlers/move_guard.rs @@ -133,16 +133,16 @@ pub(crate) fn move_arm_cond_to_match_guard( }; let then_arm_end = match_arm.syntax().text_range().end(); let indent_level = match_arm.indent_level(); - let spaces = " ".repeat(indent_level.0 as _); + let spaces = indent_level; let mut first = true; for (cond, block) in conds_blocks { if !first { - edit.insert(then_arm_end, format!("\n{}", spaces)); + edit.insert(then_arm_end, format!("\n{spaces}")); } else { first = false; } - let guard = format!("{} if {} => ", match_pat, cond.syntax().text()); + let guard = format!("{match_pat} if {cond} => "); edit.insert(then_arm_end, guard); let only_expr = block.statements().next().is_none(); match &block.tail_expr() { @@ -158,7 +158,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } if let Some(e) = tail { cov_mark::hit!(move_guard_ifelse_else_tail); - let guard = format!("\n{}{} => ", spaces, match_pat); + let guard = format!("\n{spaces}{match_pat} => "); edit.insert(then_arm_end, guard); let only_expr = e.statements().next().is_none(); match &e.tail_expr() { @@ -183,7 +183,7 @@ pub(crate) fn move_arm_cond_to_match_guard( { cov_mark::hit!(move_guard_ifelse_has_wildcard); } - _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)), + _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")), } } }, diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index 7468318a594af..a7c605325ea69 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut buf = String::from("./"); match parent_module.name(ctx.db()) { Some(name) if !parent_module.is_mod_rs(ctx.db()) => { - format_to!(buf, "{}/", name) + format_to!(buf, "{name}/") } _ => (), } @@ -82,7 +82,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> items }; - let buf = format!("mod {};", module_name); + let buf = format!("mod {module_name};"); let replacement_start = match module_ast.mod_token() { Some(mod_token) => mod_token.text_range(), diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs index a909ce8b26791..076d25411a818 100644 --- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -40,11 +40,11 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let target = source_file.syntax().text_range(); let module_name = module.name(ctx.db())?.to_string(); - let path = format!("./{}/mod.rs", module_name); + let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( AssistId("move_to_mod_rs", AssistKind::Refactor), - format!("Convert {}.rs to {}/mod.rs", module_name, module_name), + format!("Convert {module_name}.rs to {module_name}/mod.rs"), target, |builder| { builder.move_file(ctx.file_id(), dst); diff --git a/crates/ide-assists/src/handlers/number_representation.rs b/crates/ide-assists/src/handlers/number_representation.rs index 424db7437a743..7e3fef516bfd8 100644 --- a/crates/ide-assists/src/handlers/number_representation.rs +++ b/crates/ide-assists/src/handlers/number_representation.rs @@ -38,7 +38,7 @@ pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_> converted.push_str(suffix); let group_id = GroupLabel("Reformat number literal".into()); - let label = format!("Convert {} to {}", literal, converted); + let label = format!("Convert {literal} to {converted}"); let range = literal.syntax().text_range(); acc.add_group( &group_id, diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index e57d1d065d622..1ea87429c5092 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -54,7 +54,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> acc.add( AssistId("qualify_method_call", AssistKind::RefactorInline), - format!("Qualify `{}` method call", ident.text()), + format!("Qualify `{ident}` method call"), range, |builder| { qualify_candidate.qualify( diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index 4b2af550bc5e4..e759e1561cbd0 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -118,14 +118,14 @@ impl QualifyCandidate<'_> { match self { QualifyCandidate::QualifierStart(segment, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{}{}::{}", import, generics, segment)); + replacer(format!("{import}{generics}::{segment}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{}{}", import, generics)); + replacer(format!("{import}{generics}")); } QualifyCandidate::TraitAssocItem(qualifier, segment) => { - replacer(format!("<{} as {}>::{}", qualifier, import, segment)); + replacer(format!("<{qualifier} as {import}>::{segment}")); } QualifyCandidate::TraitMethod(db, mcall_expr) => { Self::qualify_trait_method(db, mcall_expr, replacer, import, item); @@ -155,16 +155,11 @@ impl QualifyCandidate<'_> { hir::Access::Exclusive => make::expr_ref(receiver, true), hir::Access::Owned => receiver, }; - replacer(format!( - "{}::{}{}{}", - import, - method_name, - generics, - match arg_list { - Some(args) => make::arg_list(iter::once(receiver).chain(args)), - None => make::arg_list(iter::once(receiver)), - } - )); + let arg_list = match arg_list { + Some(args) => make::arg_list(iter::once(receiver).chain(args)), + None => make::arg_list(iter::once(receiver)), + }; + replacer(format!("{import}::{method_name}{generics}{arg_list}")); } Some(()) } @@ -218,15 +213,17 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { } } .text(); - GroupLabel(format!("Qualify {}", name)) + GroupLabel(format!("Qualify {name}")) } fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { + let import_path = &import.import_path; + match candidate { ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => { - format!("Qualify as `{}`", import.import_path) + format!("Qualify as `{import_path}`") } - _ => format!("Qualify with `{}`", import.import_path), + _ => format!("Qualify with `{import_path}`"), } } diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index dbe8cb7bf031f..c9bc25b27a5ed 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs @@ -34,13 +34,10 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let hashes = "#".repeat(required_hashes(&value).max(1)); if matches!(value, Cow::Borrowed(_)) { // Avoid replacing the whole string to better position the cursor. - edit.insert(token.syntax().text_range().start(), format!("r{}", hashes)); + edit.insert(token.syntax().text_range().start(), format!("r{hashes}")); edit.insert(token.syntax().text_range().end(), hashes); } else { - edit.replace( - token.syntax().text_range(), - format!("r{}\"{}\"{}", hashes, value, hashes), - ); + edit.replace(token.syntax().text_range(), format!("r{hashes}\"{value}\"{hashes}")); } }, ) @@ -83,7 +80,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O } } - edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); + edit.replace(token.syntax().text_range(), format!("\"{escaped}\"")); }, ) } diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index afaa7c933c739..3d9cbff177ba9 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -102,7 +102,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }; ( macro_call.syntax().text_range(), - if wrap { format!("({})", expr) } else { expr.to_string() }, + if wrap { format!("({expr})") } else { expr.to_string() }, ) } // dbg!(expr0, expr1, ...) @@ -127,8 +127,8 @@ mod tests { fn check(ra_fixture_before: &str, ra_fixture_after: &str) { check_assist( remove_dbg, - &format!("fn main() {{\n{}\n}}", ra_fixture_before), - &format!("fn main() {{\n{}\n}}", ra_fixture_after), + &format!("fn main() {{\n{ra_fixture_before}\n}}"), + &format!("fn main() {{\n{ra_fixture_after}\n}}"), ); } diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 9fd5e1886d206..f9ba289ee175f 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -124,7 +124,7 @@ fn add_assist( ) -> Option<()> { let target = attr.syntax().text_range(); let annotated_name = adt.name()?; - let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name); + let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add( AssistId("replace_derive_with_manual_impl", AssistKind::Refactor), @@ -158,11 +158,8 @@ fn add_assist( } } - builder.insert_snippet( - cap, - insert_pos, - format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), - ) + let rendered = render_snippet(cap, impl_def.syntax(), cursor); + builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}")) } }; }, diff --git a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs index 7d91be6210136..77382056c1833 100644 --- a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs +++ b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs @@ -62,7 +62,7 @@ pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_> acc.add( AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite), - format!("Replace {} with {}", name.text(), replace), + format!("Replace {name} with {replace}"), call.syntax().text_range(), |builder| { builder.replace(name.syntax().text_range(), replace); @@ -138,7 +138,7 @@ pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_> acc.add( AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite), - format!("Replace {} with {}", name.text(), replace), + format!("Replace {name} with {replace}"), call.syntax().text_range(), |builder| { builder.replace(name.syntax().text_range(), replace); diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 521447c26dfbe..c177adc7a10d7 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -79,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( "Replace turbofish with explicit type", TextRange::new(initializer_start, turbofish_range.end()), |builder| { - builder.insert(ident_range.end(), format!(": {}", returned_type)); + builder.insert(ident_range.end(), format!(": {returned_type}")); builder.delete(turbofish_range); }, ); diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs index 25c58d086e977..d09614c51127e 100644 --- a/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -69,13 +69,13 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents)) + zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n")) } edit.replace(parent.text_range(), zipped_decls.trim()); } else { let mut zipped_decls = String::new(); for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents)); + zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n")); } edit.replace(parent.text_range(), zipped_decls.trim()); } diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index 83446387db1cf..b6c489eb62eef 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -76,11 +76,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< match ctx.config.snippet_cap { Some(cap) => { - let snippet = format!("Result<{}, ${{0:_}}>", type_ref); + let snippet = format!("Result<{type_ref}, ${{0:_}}>"); builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) } None => builder - .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), + .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")), } }, ) From a02ec4cf1817b6ec7f154e11521cb76507cd4a3c Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Wed, 12 Oct 2022 18:39:22 +0200 Subject: [PATCH 0027/1080] remove HRTB from `[T]::is_sorted_by{,_key}` --- library/core/src/slice/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 56133f346ae9e..e874b57636894 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3724,9 +3724,9 @@ impl [T] { /// [`is_sorted`]: slice::is_sorted #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] #[must_use] - pub fn is_sorted_by(&self, mut compare: F) -> bool + pub fn is_sorted_by<'a, F>(&'a self, mut compare: F) -> bool where - F: FnMut(&T, &T) -> Option, + F: FnMut(&'a T, &'a T) -> Option, { self.iter().is_sorted_by(|a, b| compare(*a, *b)) } @@ -3750,9 +3750,9 @@ impl [T] { #[inline] #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] #[must_use] - pub fn is_sorted_by_key(&self, f: F) -> bool + pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool where - F: FnMut(&T) -> K, + F: FnMut(&'a T) -> K, K: PartialOrd, { self.iter().is_sorted_by_key(f) From f3d7b39cdf483ae543757fbef369c166650e66f2 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Wed, 12 Oct 2022 18:54:14 +0200 Subject: [PATCH 0028/1080] add regression test --- .../slice_is_sorted_by_borrow.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/ui/array-slice-vec/slice_is_sorted_by_borrow.rs diff --git a/src/test/ui/array-slice-vec/slice_is_sorted_by_borrow.rs b/src/test/ui/array-slice-vec/slice_is_sorted_by_borrow.rs new file mode 100644 index 0000000000000..073280d0fab1f --- /dev/null +++ b/src/test/ui/array-slice-vec/slice_is_sorted_by_borrow.rs @@ -0,0 +1,20 @@ +// check-pass +// regression test for https://github.com/rust-lang/rust/issues/53485#issuecomment-885393452 + +#![feature(is_sorted)] + +struct A { + name: String, +} + +fn main() { + let a = &[ + A { + name: "1".to_string(), + }, + A { + name: "2".to_string(), + }, + ]; + assert!(a.is_sorted_by_key(|a| a.name.as_str())); +} From 1450710f12f74ce6bd55c44e4170fbd352846f3d Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Sat, 15 Oct 2022 23:52:40 +0300 Subject: [PATCH 0029/1080] Enable test no_std_main_recursion --- tests/ui/crate_level_checks/no_std_main_recursion.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 4a5c597dda51f..e1c9fe30a9dfb 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,6 +1,5 @@ // compile-flags: -Clink-arg=-nostartfiles // ignore-macos -// ignore-windows #![feature(lang_items, start, libc)] #![no_std] From c5ad97da258bb47cba2f888cee73b0f4b5289d58 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 16 Oct 2022 14:53:21 +0100 Subject: [PATCH 0030/1080] Use IsTerminal in rustc_errors --- Cargo.lock | 1 - compiler/rustc_errors/Cargo.toml | 1 - compiler/rustc_errors/src/emitter.rs | 6 +++--- compiler/rustc_errors/src/lib.rs | 1 + 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5acab8139c98..6c0bb32dec71e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3452,7 +3452,6 @@ name = "rustc_errors" version = "0.0.0" dependencies = [ "annotate-snippets", - "atty", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 7803a0792e12c..eb560a68b5697 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -18,7 +18,6 @@ rustc_target = { path = "../rustc_target" } rustc_hir = { path = "../rustc_hir" } rustc_lint_defs = { path = "../rustc_lint_defs" } unicode-width = "0.1.4" -atty = "0.2" termcolor = "1.0" annotate-snippets = "0.9" termize = "0.1.1" diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index cd6413bc3ec62..12a93fbedf900 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -28,8 +28,8 @@ use rustc_error_messages::FluentArgs; use rustc_span::hygiene::{ExpnKind, MacroKind}; use std::borrow::Cow; use std::cmp::{max, min, Reverse}; -use std::io; use std::io::prelude::*; +use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; @@ -619,14 +619,14 @@ impl ColorConfig { fn to_color_choice(self) -> ColorChoice { match self { ColorConfig::Always => { - if atty::is(atty::Stream::Stderr) { + if io::stderr().is_terminal() { ColorChoice::Always } else { ColorChoice::AlwaysAnsi } } ColorConfig::Never => ColorChoice::Never, - ColorConfig::Auto if atty::is(atty::Stream::Stderr) => ColorChoice::Auto, + ColorConfig::Auto if io::stderr().is_terminal() => ColorChoice::Auto, ColorConfig::Auto => ColorChoice::Never, } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9fafbe4bd407e..814dba10484eb 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -5,6 +5,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(drain_filter)] #![feature(if_let_guard)] +#![feature(is_terminal)] #![feature(adt_const_params)] #![feature(let_chains)] #![feature(never_type)] From 34f61dd56718261548f58e9cd6bb9c4146825a1e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 16 Oct 2022 14:55:18 +0100 Subject: [PATCH 0031/1080] Use IsTerminal in rustc_driver --- compiler/rustc_driver/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 7d5604fcabcc9..d1d590d2d08dd 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -5,6 +5,7 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(is_terminal)] #![feature(once_cell)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] @@ -26,7 +27,6 @@ use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; -use rustc_log::stdout_isatty; use rustc_metadata::locator; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; @@ -47,7 +47,7 @@ use std::default::Default; use std::env; use std::ffi::OsString; use std::fs; -use std::io::{self, Read, Write}; +use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, catch_unwind}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; @@ -538,7 +538,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { } text.push('\n'); } - if stdout_isatty() { + if io::stdout().is_terminal() { show_content_with_pager(&text); } else { print!("{}", text); From d60ba29b10e2c4741bbd8701c74b85c09b70e03d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 16 Oct 2022 14:55:44 +0100 Subject: [PATCH 0032/1080] Use IsTerminal in rustc_log --- Cargo.lock | 1 - compiler/rustc_log/Cargo.toml | 1 - compiler/rustc_log/src/lib.rs | 7 ++++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c0bb32dec71e..bf5ff830acc47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3724,7 +3724,6 @@ dependencies = [ name = "rustc_log" version = "0.0.0" dependencies = [ - "atty", "rustc_span", "tracing", "tracing-subscriber", diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 1b2cde605556d..3c50827c1abc3 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [dependencies] -atty = "0.2" tracing = "0.1.28" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.2.0" diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 458f5e87baeac..ddf29c488c933 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -40,10 +40,11 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![feature(is_terminal)] use std::env::{self, VarError}; use std::fmt::{self, Display}; -use std::io; +use std::io::{self, IsTerminal}; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::layer::SubscriberExt; @@ -93,11 +94,11 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { } pub fn stdout_isatty() -> bool { - atty::is(atty::Stream::Stdout) + io::stdout().is_terminal() } pub fn stderr_isatty() -> bool { - atty::is(atty::Stream::Stderr) + io::stderr().is_terminal() } #[derive(Debug)] From e3d44dd4bdd901d284ec47a29e0bb7d825f9786b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 16 Oct 2022 14:56:03 +0100 Subject: [PATCH 0033/1080] Use IsTerminal in librustdoc --- Cargo.lock | 1 - src/librustdoc/Cargo.toml | 1 - src/librustdoc/lib.rs | 5 +++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf5ff830acc47..6630b6479dc48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4272,7 +4272,6 @@ version = "0.0.0" dependencies = [ "arrayvec", "askama", - "atty", "expect-test", "itertools", "minifier", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 7bc35c7d5516f..63ccee14caa9d 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,7 +9,6 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } askama = { version = "0.11", default-features = false, features = ["config"] } -atty = "0.2" itertools = "0.10.1" minifier = "0.2.2" once_cell = "1.10.0" diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ef01b854e5a69..7276b9ce0ee7d 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -8,6 +8,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(drain_filter)] +#![feature(is_terminal)] #![feature(let_chains)] #![feature(test)] #![feature(never_type)] @@ -69,7 +70,7 @@ extern crate jemalloc_sys; use std::default::Default; use std::env::{self, VarError}; -use std::io; +use std::io::{self, IsTerminal}; use std::process; use rustc_driver::abort_on_err; @@ -180,7 +181,7 @@ fn init_logging() { let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() { Ok("always") => true, Ok("never") => false, - Ok("auto") | Err(VarError::NotPresent) => atty::is(atty::Stream::Stdout), + Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(), Ok(value) => early_error( ErrorOutputType::default(), &format!("invalid log color value '{}': expected one of always, never, or auto", value), From df5d035f5199eab85dac327cbbd30a3ddfe29abd Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Tue, 18 Oct 2022 14:11:02 +0000 Subject: [PATCH 0034/1080] mark sys_common::once::generic::Once::new const-stable --- library/std/src/sys_common/once/generic.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys_common/once/generic.rs b/library/std/src/sys_common/once/generic.rs index acf5f247164a7..d953a67459234 100644 --- a/library/std/src/sys_common/once/generic.rs +++ b/library/std/src/sys_common/once/generic.rs @@ -107,6 +107,7 @@ struct WaiterQueue<'a> { impl Once { #[inline] + #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")] pub const fn new() -> Once { Once { state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)) } } From 831b99436c3dc47eafdaf363acd1d4e9e230551d Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Wed, 19 Oct 2022 00:08:20 +0200 Subject: [PATCH 0035/1080] Implement -Ztrack-diagnostics --- clippy_lints/src/doc.rs | 1 + src/driver.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 36dc7e3396b82..9e2facf0f63b7 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -691,6 +691,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, None, false, + false, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/driver.rs b/src/driver.rs index b12208ac62a88..ae54b2078a65b 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -179,6 +179,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { false, None, false, + false, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); From b78509e2f29c12594cb8c9633ef89383f1536e5a Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 19 Oct 2022 11:46:26 +0800 Subject: [PATCH 0036/1080] Add testcase for next_point, fix more trivial issues in find_width_of_character_at_span --- clippy_utils/src/sugg.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3c5dd92b9cd6d..3347342e412b6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -769,8 +769,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) { let mut remove_span = item; - let hi = cx.sess().source_map().next_point(remove_span).hi(); - let fmpos = cx.sess().source_map().lookup_byte_offset(hi); + let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.hi()); if let Some(ref src) = fmpos.sf.src { let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n'); From 4dfc7b20250b040b34953ccf71b1bbef77a75f7d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Oct 2022 11:34:00 -0700 Subject: [PATCH 0037/1080] Fixup a few tests needing asm support --- tests/ui/entry.fixed | 1 + tests/ui/entry.rs | 1 + tests/ui/entry.stderr | 20 ++++++++-------- tests/ui/missing_doc.rs | 1 + tests/ui/missing_doc.stderr | 48 ++++++++++++++++++------------------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index e43635abcd112..79c29c04e0596 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index d999b3b7dc80d..2d7985457d8b4 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 2ef9966525cef..2c4c49d2522ca 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:24:5 + --> $DIR/entry.rs:25:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::map-entry` implied by `-D warnings` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:29:5 + --> $DIR/entry.rs:30:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -32,7 +32,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:38:5 + --> $DIR/entry.rs:39:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -55,7 +55,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:47:5 + --> $DIR/entry.rs:48:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -79,7 +79,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:57:5 + --> $DIR/entry.rs:58:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -96,7 +96,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:63:5 + --> $DIR/entry.rs:64:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -122,7 +122,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:75:5 + --> $DIR/entry.rs:76:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -146,7 +146,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:85:5 + --> $DIR/entry.rs:86:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -187,7 +187,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:119:5 + --> $DIR/entry.rs:120:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -195,7 +195,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:151:5 + --> $DIR/entry.rs:152:5 | LL | / if !m.contains_key(&k) { LL | | let x = (String::new(), String::new()); diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 29cc026a8fd39..590ad63c90be3 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,3 +1,4 @@ +// needs-asm-support // aux-build: proc_macro_with_span.rs #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 6c8e66f464377..d3bef28bf64c6 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> $DIR/missing_doc.rs:15:1 + --> $DIR/missing_doc.rs:16:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | type Typedef = String; = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` error: missing documentation for a type alias - --> $DIR/missing_doc.rs:16:1 + --> $DIR/missing_doc.rs:17:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:18:1 + --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:19:1 + --> $DIR/missing_doc.rs:20:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:23:1 + --> $DIR/missing_doc.rs:24:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 + --> $DIR/missing_doc.rs:25:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:38:1 + --> $DIR/missing_doc.rs:39:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -46,31 +46,31 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:39:5 + --> $DIR/missing_doc.rs:40:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:12 + --> $DIR/missing_doc.rs:40:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:22 + --> $DIR/missing_doc.rs:40:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:40:5 + --> $DIR/missing_doc.rs:41:5 | LL | BarB, | ^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:43:1 + --> $DIR/missing_doc.rs:44:1 | LL | / pub enum PubBaz { LL | | PubBazA { a: isize }, @@ -78,43 +78,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:44:5 + --> $DIR/missing_doc.rs:45:5 | LL | PubBazA { a: isize }, | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:44:15 + --> $DIR/missing_doc.rs:45:15 | LL | PubBazA { a: isize }, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:64:1 + --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:71:1 + --> $DIR/missing_doc.rs:72:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:73:1 + --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:80:1 + --> $DIR/missing_doc.rs:81:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:82:1 + --> $DIR/missing_doc.rs:83:1 | LL | / mod internal_impl { LL | | /// dox @@ -126,31 +126,31 @@ LL | | } | |_^ error: missing documentation for a function - --> $DIR/missing_doc.rs:85:5 + --> $DIR/missing_doc.rs:86:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 + --> $DIR/missing_doc.rs:87:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 + --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:92:9 + --> $DIR/missing_doc.rs:93:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 + --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 56506730c827e02a69cb803fcaa7e912cf25826b Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 11 Oct 2022 16:17:59 +0000 Subject: [PATCH 0038/1080] Implement assertions and fixes to not emit empty spans without suggestions --- clippy_lints/src/manual_assert.rs | 12 ++--- clippy_lints/src/needless_late_init.rs | 11 +++-- tests/ui/manual_assert.edition2018.stderr | 55 ++++------------------- tests/ui/manual_assert.edition2021.stderr | 55 ++++------------------- 4 files changed, 30 insertions(+), 103 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 825ec84b4a812..b8ed9b9ec18f7 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -69,11 +69,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { "only a `panic!` in `if`-then statement", |diag| { // comments can be noisy, do not show them to the user - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + } diag.span_suggestion( expr.span, "try instead", diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9d26e5900866c..67debe7e08af6 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -180,10 +180,13 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() .flat_map(|assignment| { - [ - assignment.span.until(assignment.rhs_span), - assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - ] + let mut spans = vec![assignment.span.until(assignment.rhs_span)]; + + if assignment.rhs_span.hi() != assignment.span.hi() { + spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); + } + + spans }) .map(|span| (span, String::new())) .collect::>(); diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 7718588fdf6f4..237638ee1344c 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 7718588fdf6f4..237638ee1344c 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 From 8ebc96a8a98f9e88259400f02ce3fabe1f526570 Mon Sep 17 00:00:00 2001 From: not_joon Date: Thu, 20 Oct 2022 22:06:00 +0900 Subject: [PATCH 0039/1080] fix broken links in guide.md --- docs/dev/guide.md | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/dev/guide.md b/docs/dev/guide.md index 52a13da31c5d3..56a68ef043790 100644 --- a/docs/dev/guide.md +++ b/docs/dev/guide.md @@ -338,7 +338,7 @@ The algorithm for building a tree of modules is to start with a crate root declarations and recursively process child modules. This is handled by the [`module_tree_query`], with two slight variations. -[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L116-L123 +[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L115-L133 First, rust-analyzer builds a module tree for all crates in a source root simultaneously. The main reason for this is historical (`module_tree` predates @@ -361,7 +361,7 @@ the same, we don't have to re-execute [`module_tree_query`]. In fact, we only need to re-execute it when we add/remove new files or when we change mod declarations. -[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L41 +[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41 We store the resulting modules in a `Vec`-based indexed arena. The indices in the arena becomes module IDs. And this brings us to the next topic: @@ -389,8 +389,8 @@ integers which can "intern" a location and return an integer ID back. The salsa database we use includes a couple of [interners]. How to "garbage collect" unused locations is an open question. -[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/loc2id.rs#L65-L71 -[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/db.rs#L22-L23 +[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/loc2id.rs#L65-L71 +[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23 For example, we use `LocationInterner` to assign IDs to definitions of functions, structs, enums, etc. The location, [`DefLoc`] contains two bits of information: @@ -404,7 +404,7 @@ using offsets, text ranges or syntax trees as keys and values for queries. What we do instead is we store "index" of the item among all of the items of a file (so, a positional based ID, but localized to a single file). -[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L127-L139 +[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L129-L139 One thing we've glossed over for the time being is support for macros. We have only proof of concept handling of macros at the moment, but they are extremely @@ -437,7 +437,7 @@ terms of `HirFileId`! This does not recur infinitely though: any chain of `HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file actually written by the user. -[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L18-L125 +[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L31-L93 Now that we understand how to identify a definition, in a source or in a macro-generated file, we can discuss name resolution a bit. @@ -451,14 +451,13 @@ each module into a position-independent representation which does not change if we modify bodies of the items. After that we [loop] resolving all imports until we've reached a fixed point. -[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L113-L117 -[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres.rs#L186-L196 - +[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L147 +[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196 And, given all our preparation with IDs and a position-independent representation, it is satisfying to [test] that typing inside function body does not invalidate name resolution results. -[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/tests.rs#L376 +[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/tests.rs#L376 An interesting fact about name resolution is that it "erases" all of the intermediate paths from the imports: in the end, we know which items are defined @@ -493,10 +492,10 @@ there's an intermediate [projection query] which returns only the first position-independent part of the lowering. The result of this query is stable. Naturally, name resolution [uses] this stable projection query. -[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59 -[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59 -[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L97-L103 -[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/query_definitions.rs#L49 +[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 +[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 +[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103 +[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49 ## Type inference @@ -518,10 +517,10 @@ construct a mapping from `ExprId`s to types. [@flodiebold]: https://github.com/flodiebold [#327]: https://github.com/rust-lang/rust-analyzer/pull/327 -[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs -[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L13-L15 -[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L41-L44 -[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ty.rs#L1208-L1223 +[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs +[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15 +[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44 +[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223 ## Tying it all together: completion @@ -563,10 +562,11 @@ the type to completion. [catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442 [the handler]: https://salsa.zulipchat.com/#narrow/stream/181542-rfcs.2Fsalsa-query-group/topic/design.20next.20steps [ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L439-L444 -[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L46-L62 -[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L14-L37 -["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L72-L75 -[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L116-L120 -[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L123 -[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L52-L59 -[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/complete_dot.rs#L6-L22 +[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444 +[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62 +[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37 +["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L72-L75 +[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L116-L120 +[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L123 +[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L52-L59 +[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/complete_dot.rs#L6-L22 From c87567cbfacae2f55983c2749b147391061530a0 Mon Sep 17 00:00:00 2001 From: b4den Date: Thu, 20 Oct 2022 15:44:25 +0100 Subject: [PATCH 0040/1080] Add context to compiler error message Changed `creates a temporary which is freed while still in use` to `creates a temporary value which is freed while still in use` --- compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 314119b6e8142..d632dd843fab5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1549,7 +1549,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let mut err = self.temporary_value_borrowed_for_too_long(proper_span); - err.span_label(proper_span, "creates a temporary which is freed while still in use"); + err.span_label(proper_span, "creates a temporary value which is freed while still in use"); err.span_label(drop_span, "temporary value is freed at the end of this statement"); match explanation { From 6cb65646b83cc7d79d0b88453cc20ce1a475d693 Mon Sep 17 00:00:00 2001 From: b4den Date: Thu, 20 Oct 2022 16:43:27 +0100 Subject: [PATCH 0041/1080] Update tests to match error message changes --- library/core/src/pin.rs | 2 +- .../borrowck-borrowed-uniq-rvalue-2.stderr | 2 +- .../borrowck-borrowed-uniq-rvalue.stderr | 2 +- src/test/ui/borrowck/issue-11493.stderr | 2 +- src/test/ui/borrowck/issue-17545.stderr | 2 +- src/test/ui/borrowck/issue-36082.fixed | 2 +- src/test/ui/borrowck/issue-36082.rs | 2 +- src/test/ui/borrowck/issue-36082.stderr | 2 +- src/test/ui/cleanup-rvalue-scopes-cf.stderr | 14 +++---- .../const-eval-intrinsic-promotion.stderr | 2 +- .../dont_promote_unstable_const_fn.stderr | 6 +-- ...omote_unstable_const_fn_cross_crate.stderr | 4 +- .../const-eval/promoted_const_fn_fail.stderr | 2 +- ...omoted_const_fn_fail_deny_const_err.stderr | 2 +- .../const-eval/promoted_raw_ptr_ops.stderr | 8 ++-- .../transmute-const-promotion.stderr | 2 +- .../consts/const-eval/union_promotion.stderr | 2 +- .../ui/consts/const-int-conversion.stderr | 14 +++---- .../ui/consts/const-int-overflowing.stderr | 6 +-- src/test/ui/consts/const-int-rotate.stderr | 4 +- src/test/ui/consts/const-int-sign.stderr | 4 +- src/test/ui/consts/const-int-wrapping.stderr | 10 ++--- .../const-mut-refs/mut_ref_in_final.stderr | 10 ++--- src/test/ui/consts/const-ptr-nonnull.stderr | 4 +- src/test/ui/consts/const-ptr-unique.stderr | 2 +- .../control-flow/interior-mutability.stderr | 6 +-- src/test/ui/consts/issue-54224.stderr | 4 +- .../ui/consts/min_const_fn/promotion.stderr | 12 +++--- src/test/ui/consts/promote-not.stderr | 40 +++++++++---------- src/test/ui/consts/promote_const_let.stderr | 2 +- src/test/ui/consts/promoted-const-drop.stderr | 4 +- src/test/ui/consts/qualif-union.stderr | 10 ++--- .../ui/generator/auto-trait-regions.stderr | 4 +- .../bugs/hrtb-implied-1.stderr | 2 +- src/test/ui/issues/issue-47184.stderr | 2 +- src/test/ui/issues/issue-52049.stderr | 2 +- .../lifetimes/borrowck-let-suggestion.stderr | 2 +- .../ui/nll/borrowed-temporary-error.stderr | 2 +- .../issue-57265-return-type-wf-check.stderr | 2 +- .../ui/nll/user-annotations/patterns.stderr | 6 +-- ...fetime_errors_on_promotion_misusage.stderr | 4 +- ...egions-free-region-ordering-caller1.stderr | 2 +- .../regions-var-type-out-of-scope.stderr | 2 +- .../span/borrowck-let-suggestion-suffixes.rs | 6 +-- .../borrowck-let-suggestion-suffixes.stderr | 6 +-- .../ui/span/borrowck-ref-into-rvalue.stderr | 2 +- src/test/ui/span/issue-15480.stderr | 2 +- ...ions-close-over-borrowed-ref-in-obj.stderr | 2 +- src/test/ui/span/slice-borrow.stderr | 2 +- src/test/ui/static/static-drop-scope.stderr | 4 +- .../ui/static/static-reference-to-fn-2.stderr | 6 +-- src/test/ui/static/static-region-bound.stderr | 2 +- src/test/ui/statics/issue-44373.stderr | 2 +- 53 files changed, 127 insertions(+), 127 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index ccef35b45325a..f0258640e2aec 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1059,7 +1059,7 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// 8 | let x: Pin<&mut Foo> = { /// | - borrow later stored here /// 9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); -/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use /// 10 | x /// 11 | }; // <- Foo is dropped /// | - temporary value is freed at the end of this statement diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr index a6af129bf39f1..4eeec09b91040 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x = defer(&vec!["Goodbye", "world!"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | x.x[0]; | ------ borrow later used here | diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr index dea8ac90bec2e..c62d5f903c8d8 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | buggy_map.insert(42, &*Box::new(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | buggy_map.insert(43, &*tmp); | --------------------------- borrow later used here diff --git a/src/test/ui/borrowck/issue-11493.stderr b/src/test/ui/borrowck/issue-11493.stderr index a5d1f2816f1ca..2720b09b0fc5f 100644 --- a/src/test/ui/borrowck/issue-11493.stderr +++ b/src/test/ui/borrowck/issue-11493.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let y = x.as_ref().unwrap_or(&id(5)); | ^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | let _ = &y; | -- borrow later used here | diff --git a/src/test/ui/borrowck/issue-17545.stderr b/src/test/ui/borrowck/issue-17545.stderr index 79a1e09bd7cc6..3ae7e64d202b6 100644 --- a/src/test/ui/borrowck/issue-17545.stderr +++ b/src/test/ui/borrowck/issue-17545.stderr @@ -5,7 +5,7 @@ LL | pub fn foo<'a, F: Fn(&'a ())>(bar: F) { | -- lifetime `'a` defined here LL | / bar.call(( LL | | &id(()), - | | ^^^^^^ creates a temporary which is freed while still in use + | | ^^^^^^ creates a temporary value which is freed while still in use LL | | )); | | -- temporary value is freed at the end of this statement | |______| diff --git a/src/test/ui/borrowck/issue-36082.fixed b/src/test/ui/borrowck/issue-36082.fixed index 8640ca7a50964..8fc963a85664e 100644 --- a/src/test/ui/borrowck/issue-36082.fixed +++ b/src/test/ui/borrowck/issue-36082.fixed @@ -10,7 +10,7 @@ fn main() { let val: &_ = binding.0; //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here diff --git a/src/test/ui/borrowck/issue-36082.rs b/src/test/ui/borrowck/issue-36082.rs index 877d372fb8484..20f66b4d45de4 100644 --- a/src/test/ui/borrowck/issue-36082.rs +++ b/src/test/ui/borrowck/issue-36082.rs @@ -9,7 +9,7 @@ fn main() { let val: &_ = x.borrow().0; //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here diff --git a/src/test/ui/borrowck/issue-36082.stderr b/src/test/ui/borrowck/issue-36082.stderr index 4bd586db1cdce..a6357f8182fc2 100644 --- a/src/test/ui/borrowck/issue-36082.stderr +++ b/src/test/ui/borrowck/issue-36082.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let val: &_ = x.borrow().0; | ^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | println!("{}", val); | --- borrow later used here diff --git a/src/test/ui/cleanup-rvalue-scopes-cf.stderr b/src/test/ui/cleanup-rvalue-scopes-cf.stderr index 40f14c389842e..425cd75141cec 100644 --- a/src/test/ui/cleanup-rvalue-scopes-cf.stderr +++ b/src/test/ui/cleanup-rvalue-scopes-cf.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x1 = arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -21,7 +21,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x2 = AddFlags(1).get(); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -38,7 +38,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x3 = &*arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -55,7 +55,7 @@ error[E0716]: temporary value dropped while borrowed LL | let ref x4 = *arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -72,7 +72,7 @@ error[E0716]: temporary value dropped while borrowed LL | let &ref x5 = arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -89,7 +89,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x6 = AddFlags(1).get(); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -106,7 +106,7 @@ error[E0716]: temporary value dropped while borrowed LL | let StackBox { f: x7 } = StackBox { f: AddFlags(1).get() }; | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here diff --git a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr index 78143042ece7b..ed6a6ee6e0fd9 100644 --- a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr +++ b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x: &'static usize = | -------------- type annotation requires that borrow lasts for `'static` LL | &std::intrinsics::size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr index 69e3ca716a909..2e697b219c5a4 100644 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr +++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr @@ -10,7 +10,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:17:28 | LL | let _: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -20,7 +20,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:21:28 | LL | let _: &'static u32 = &meh(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -31,7 +31,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:22:26 | LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr index 129f06151074f..aa742d784e035 100644 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr +++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:8:28 | LL | let _: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _x: &'static u32 = &foo(); @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:9:29 | LL | let _x: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr index 596fa090d976f..2d4e7c83d3e4e 100644 --- a/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr +++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_const_fn_fail.rs:17:27 | LL | let x: &'static u8 = &(bar() + 1); - | ----------- ^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr index 63dc43a41a8fc..9ebae3a18a32f 100644 --- a/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr +++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_const_fn_fail_deny_const_err.rs:18:27 | LL | let x: &'static u8 = &(bar() + 1); - | ----------- ^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... diff --git a/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr index 8ac60da38634b..01fcf2ec21342 100644 --- a/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr +++ b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:2:29 | LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:4:30 | LL | let y: &'static usize = &(&1 as *const i32 as usize + 1); - | -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:6:28 | LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) }); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:8:29 | LL | let a: &'static bool = &(main as fn() == main as fn()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/transmute-const-promotion.stderr b/src/test/ui/consts/const-eval/transmute-const-promotion.stderr index 15b9b56ea6606..434a957f64840 100644 --- a/src/test/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/src/test/ui/consts/const-eval/transmute-const-promotion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/transmute-const-promotion.rs:4:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/union_promotion.stderr b/src/test/ui/consts/const-eval/union_promotion.stderr index 70808c520d3f9..42f17de200344 100644 --- a/src/test/ui/consts/const-eval/union_promotion.stderr +++ b/src/test/ui/consts/const-eval/union_promotion.stderr @@ -7,7 +7,7 @@ LL | let x: &'static bool = &unsafe { | | type annotation requires that borrow lasts for `'static` LL | | Foo { a: &1 }.b == Foo { a: &2 }.b LL | | }; - | |_____^ creates a temporary which is freed while still in use + | |_____^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/const-int-conversion.stderr b/src/test/ui/consts/const-int-conversion.stderr index 61162a792262b..5dd757e3f5ee1 100644 --- a/src/test/ui/consts/const-int-conversion.stderr +++ b/src/test/ui/consts/const-int-conversion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:2:28 | LL | let x: &'static i32 = &(5_i32.reverse_bits()); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:4:28 | LL | let y: &'static i32 = &(i32::from_be_bytes([0x12, 0x34, 0x56, 0x78])); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:6:28 | LL | let z: &'static i32 = &(i32::from_le_bytes([0x12, 0x34, 0x56, 0x78])); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:8:28 | LL | let a: &'static i32 = &(i32::from_be(i32::from_ne_bytes([0x80, 0, 0, 0]))); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:10:29 | LL | let b: &'static [u8] = &(0x12_34_56_78_i32.to_be_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -57,7 +57,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:12:29 | LL | let c: &'static [u8] = &(0x12_34_56_78_i32.to_le_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -68,7 +68,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:14:29 | LL | let d: &'static [u8] = &(i32::MIN.to_be().to_ne_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-overflowing.stderr b/src/test/ui/consts/const-int-overflowing.stderr index 56c7f7f092d66..7d3689e6ec7d7 100644 --- a/src/test/ui/consts/const-int-overflowing.stderr +++ b/src/test/ui/consts/const-int-overflowing.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:2:36 | LL | let x: &'static (i32, bool) = &(5_i32.overflowing_add(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:4:36 | LL | let y: &'static (i32, bool) = &(5_i32.overflowing_sub(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:6:36 | LL | let z: &'static (i32, bool) = &(5_i32.overflowing_mul(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-rotate.stderr b/src/test/ui/consts/const-int-rotate.stderr index ed265804bbc6c..039da1c31c575 100644 --- a/src/test/ui/consts/const-int-rotate.stderr +++ b/src/test/ui/consts/const-int-rotate.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-rotate.rs:2:28 | LL | let x: &'static i32 = &(5_i32.rotate_left(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-rotate.rs:4:28 | LL | let y: &'static i32 = &(5_i32.rotate_right(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-sign.stderr b/src/test/ui/consts/const-int-sign.stderr index 5f8fd4141804c..fc23d9d2b2942 100644 --- a/src/test/ui/consts/const-int-sign.stderr +++ b/src/test/ui/consts/const-int-sign.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-sign.rs:2:29 | LL | let x: &'static bool = &(5_i32.is_negative()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-sign.rs:4:29 | LL | let y: &'static bool = &(5_i32.is_positive()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-wrapping.stderr b/src/test/ui/consts/const-int-wrapping.stderr index 5174b72659cd7..1342fadc4055b 100644 --- a/src/test/ui/consts/const-int-wrapping.stderr +++ b/src/test/ui/consts/const-int-wrapping.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:2:28 | LL | let x: &'static i32 = &(5_i32.wrapping_add(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:4:28 | LL | let y: &'static i32 = &(5_i32.wrapping_sub(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:6:28 | LL | let z: &'static i32 = &(5_i32.wrapping_mul(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:8:28 | LL | let a: &'static i32 = &(5_i32.wrapping_shl(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:10:28 | LL | let b: &'static i32 = &(5_i32.wrapping_shr(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 3a9ce79f10ef2..78c58b5ab092b 100644 --- a/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -11,7 +11,7 @@ LL | const B3: Option<&mut i32> = Some(&mut 42); | ----------^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -21,7 +21,7 @@ LL | const B4: Option<&mut i32> = helper(&mut 42); | ------------^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -31,7 +31,7 @@ LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -41,7 +41,7 @@ LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -51,7 +51,7 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error: aborting due to 6 previous errors diff --git a/src/test/ui/consts/const-ptr-nonnull.stderr b/src/test/ui/consts/const-ptr-nonnull.stderr index 26946fb99024c..dbcb0c86052ee 100644 --- a/src/test/ui/consts/const-ptr-nonnull.stderr +++ b/src/test/ui/consts/const-ptr-nonnull.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-nonnull.rs:4:37 | LL | let x: &'static NonNull = &(NonNull::dangling()); - | --------------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | --------------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-nonnull.rs:9:37 | LL | let x: &'static NonNull = &(non_null.cast()); - | --------------------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | --------------------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-ptr-unique.stderr b/src/test/ui/consts/const-ptr-unique.stderr index 3644cf4cec7d3..83448c3e8d876 100644 --- a/src/test/ui/consts/const-ptr-unique.stderr +++ b/src/test/ui/consts/const-ptr-unique.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-unique.rs:8:33 | LL | let x: &'static *mut u32 = &(unique.as_ptr()); - | ----------------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/control-flow/interior-mutability.stderr b/src/test/ui/consts/control-flow/interior-mutability.stderr index 4f9c7d34c35f4..db2ffb91b988b 100644 --- a/src/test/ui/consts/control-flow/interior-mutability.stderr +++ b/src/test/ui/consts/control-flow/interior-mutability.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:40:26 | LL | let x: &'static _ = &X; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:41:26 | LL | let y: &'static _ = &Y; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let z: &'static _ = &Z; @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:42:26 | LL | let z: &'static _ = &Z; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/issue-54224.stderr b/src/test/ui/consts/issue-54224.stderr index 8dcb4daca3b70..55fe55759df54 100644 --- a/src/test/ui/consts/issue-54224.stderr +++ b/src/test/ui/consts/issue-54224.stderr @@ -5,7 +5,7 @@ LL | const FOO: Option<&[[u8; 3]]> = Some(&[*b"foo"]); | ------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -15,7 +15,7 @@ LL | pub const Z: Cow<'static, [ [u8; 3] ]> = Cow::Borrowed(&[*b"ABC"]); | ---------------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/min_const_fn/promotion.stderr b/src/test/ui/consts/min_const_fn/promotion.stderr index 550423c2d933c..0b8dc0ce0e903 100644 --- a/src/test/ui/consts/min_const_fn/promotion.stderr +++ b/src/test/ui/consts/min_const_fn/promotion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:11:27 | LL | let x: &'static () = &foo1(); - | ----------- ^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:12:28 | LL | let y: &'static i32 = &foo2(42); - | ------------ ^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:13:28 | LL | let z: &'static i32 = &foo3(); - | ------------ ^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:14:34 | LL | let a: &'static Cell = &foo4(); - | ------------------ ^^^^^^ creates a temporary which is freed while still in use + | ------------------ ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:15:42 | LL | let a: &'static Option> = &foo5(); - | -------------------------- ^^^^^^ creates a temporary which is freed while still in use + | -------------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let a: &'static Option> = &foo6(); @@ -57,7 +57,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:16:42 | LL | let a: &'static Option> = &foo6(); - | -------------------------- ^^^^^^ creates a temporary which is freed while still in use + | -------------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/promote-not.stderr b/src/test/ui/consts/promote-not.stderr index 0d0b0f9c689b5..b93358e8dccea 100644 --- a/src/test/ui/consts/promote-not.stderr +++ b/src/test/ui/consts/promote-not.stderr @@ -5,14 +5,14 @@ LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); | ----------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:11:18 | LL | let x = &mut [1,2,3]; - | ^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^ creates a temporary value which is freed while still in use LL | x | - using this value as a static requires that borrow lasts for `'static` LL | }; @@ -22,7 +22,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:20:32 | LL | let _x: &'static () = &foo(); - | ----------- ^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -32,7 +32,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:28:29 | LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -42,7 +42,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:33:29 | LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | }; @@ -52,7 +52,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:39:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).1; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | }; @@ -62,7 +62,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:46:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).0; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -73,7 +73,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:47:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).1; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -84,7 +84,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:50:29 | LL | let _val: &'static _ = &(1/0); - | ---------- ^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -95,7 +95,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:51:29 | LL | let _val: &'static _ = &(1/(1-1)); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -106,7 +106,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:52:29 | LL | let _val: &'static _ = &(1%0); - | ---------- ^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -117,7 +117,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:53:29 | LL | let _val: &'static _ = &(1%(1-1)); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -128,7 +128,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:54:29 | LL | let _val: &'static _ = &([1,2,3][4]+1); - | ---------- ^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -139,7 +139,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:57:29 | LL | let _val: &'static _ = &TEST_DROP; - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -150,7 +150,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:59:29 | LL | let _val: &'static _ = &&TEST_DROP; - | ---------- ^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -161,7 +161,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:59:30 | LL | let _val: &'static _ = &&TEST_DROP; - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -172,7 +172,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:62:29 | LL | let _val: &'static _ = &(&TEST_DROP,); - | ---------- ^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -183,7 +183,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:62:31 | LL | let _val: &'static _ = &(&TEST_DROP,); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -194,7 +194,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:65:29 | LL | let _val: &'static _ = &[&TEST_DROP; 1]; - | ---------- ^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -207,7 +207,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _val: &'static _ = &[&TEST_DROP; 1]; | ---------- ^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error: aborting due to 20 previous errors diff --git a/src/test/ui/consts/promote_const_let.stderr b/src/test/ui/consts/promote_const_let.stderr index c47d297c90409..975a235a6495b 100644 --- a/src/test/ui/consts/promote_const_let.stderr +++ b/src/test/ui/consts/promote_const_let.stderr @@ -19,7 +19,7 @@ LL | let x: &'static u32 = &{ LL | | let y = 42; LL | | y LL | | }; - | |_____^ creates a temporary which is freed while still in use + | |_____^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/promoted-const-drop.stderr b/src/test/ui/consts/promoted-const-drop.stderr index 184ba0ea3b377..4802834173fc5 100644 --- a/src/test/ui/consts/promoted-const-drop.stderr +++ b/src/test/ui/consts/promoted-const-drop.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted-const-drop.rs:13:26 | LL | let _: &'static A = &A(); - | ---------- ^^^ creates a temporary which is freed while still in use + | ---------- ^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _: &'static [A] = &[C]; @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted-const-drop.rs:14:28 | LL | let _: &'static [A] = &[C]; - | ------------ ^^^ creates a temporary which is freed while still in use + | ------------ ^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/qualif-union.stderr b/src/test/ui/consts/qualif-union.stderr index 8ec68ada048a5..d847cf88f505f 100644 --- a/src/test/ui/consts/qualif-union.stderr +++ b/src/test/ui/consts/qualif-union.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:28:26 | LL | let _: &'static _ = &C1; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:29:26 | LL | let _: &'static _ = &C2; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:30:26 | LL | let _: &'static _ = &C3; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:31:26 | LL | let _: &'static _ = &C4; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _: &'static _ = &C5; @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:32:26 | LL | let _: &'static _ = &C5; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr index 23324af6171a9..0b1f34aeb96bd 100644 --- a/src/test/ui/generator/auto-trait-regions.stderr +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | assert_foo(a); | - borrow later used here @@ -17,7 +17,7 @@ error[E0716]: temporary value dropped while borrowed LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | assert_foo(a); | - borrow later used here diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr index 414999881d470..1c9abc4e837c5 100644 --- a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr +++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/hrtb-implied-1.rs:31:22 | LL | let slice = &mut (); - | ^^ creates a temporary which is freed while still in use + | ^^ creates a temporary value which is freed while still in use ... LL | print_items::>(windows); | -------------------------------------- argument requires that borrow lasts for `'static` diff --git a/src/test/ui/issues/issue-47184.stderr b/src/test/ui/issues/issue-47184.stderr index f97713b4ac438..c2c7df7a333a4 100644 --- a/src/test/ui/issues/issue-47184.stderr +++ b/src/test/ui/issues/issue-47184.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _vec: Vec<&'static String> = vec![&String::new()]; | -------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-52049.stderr b/src/test/ui/issues/issue-52049.stderr index 55929d85da457..b25dbd1cb8b12 100644 --- a/src/test/ui/issues/issue-52049.stderr +++ b/src/test/ui/issues/issue-52049.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | foo(&unpromotable(5u32)); | -----^^^^^^^^^^^^^^^^^^- | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr index bbf04c98436e8..987b051b11106 100644 --- a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let mut x = vec![1].iter(); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | x.use_mut(); | ----------- borrow later used here diff --git a/src/test/ui/nll/borrowed-temporary-error.stderr b/src/test/ui/nll/borrowed-temporary-error.stderr index 2c6bd92641f60..89781d96fab26 100644 --- a/src/test/ui/nll/borrowed-temporary-error.stderr +++ b/src/test/ui/nll/borrowed-temporary-error.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/borrowed-temporary-error.rs:8:10 | LL | &(v,) - | ^^^^ creates a temporary which is freed while still in use + | ^^^^ creates a temporary value which is freed while still in use LL | LL | }); | - temporary value is freed at the end of this statement diff --git a/src/test/ui/nll/issue-57265-return-type-wf-check.stderr b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr index 20add62b91ddf..bb45575fa64c3 100644 --- a/src/test/ui/nll/issue-57265-return-type-wf-check.stderr +++ b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_, z) = foo(&"hello".to_string()); | -----^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` error: aborting due to previous error diff --git a/src/test/ui/nll/user-annotations/patterns.stderr b/src/test/ui/nll/user-annotations/patterns.stderr index 60d6e6db36326..de6f8f80fe252 100644 --- a/src/test/ui/nll/user-annotations/patterns.stderr +++ b/src/test/ui/nll/user-annotations/patterns.stderr @@ -76,7 +76,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _: Vec<&'static String> = vec![&String::new()]; | -------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -85,7 +85,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -94,7 +94,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0597]: `x` does not live long enough diff --git a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr index 4971263af08ad..fc1be052fb791 100644 --- a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr +++ b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let phantom_pinned = identity(pin!(PhantomPinned)); | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | stuff(phantom_pinned) | -------------- borrow later used here @@ -18,7 +18,7 @@ error[E0716]: temporary value dropped while borrowed LL | let phantom_pinned = { | -------------- borrow later stored here LL | let phantom_pinned = pin!(PhantomPinned); - | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | }; | - temporary value is freed at the end of this statement diff --git a/src/test/ui/regions/regions-free-region-ordering-caller1.stderr b/src/test/ui/regions/regions-free-region-ordering-caller1.stderr index 8042b1740b141..8ef7e22536bf9 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller1.stderr +++ b/src/test/ui/regions/regions-free-region-ordering-caller1.stderr @@ -5,7 +5,7 @@ LL | fn call1<'a>(x: &'a usize) { | -- lifetime `'a` defined here ... LL | let z: &'a & usize = &(&y); - | ----------- ^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'a` ... diff --git a/src/test/ui/regions/regions-var-type-out-of-scope.stderr b/src/test/ui/regions/regions-var-type-out-of-scope.stderr index 476e82f046f01..c32bbe0ee1fb4 100644 --- a/src/test/ui/regions/regions-var-type-out-of-scope.stderr +++ b/src/test/ui/regions/regions-var-type-out-of-scope.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | x = &id(3); | ^^^^^- temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | assert_eq!(*x, 3); | ----------------- borrow later used here | diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 6240f103c99b0..18abfb5c3fbec 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -18,7 +18,7 @@ fn f() { v3.push(&id('x')); // statement 6 //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| HELP consider using a `let` binding to create a longer lived value @@ -28,7 +28,7 @@ fn f() { v4.push(&id('y')); //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| NOTE consider using a `let` binding to create a longer lived value v4.use_ref(); @@ -39,7 +39,7 @@ fn f() { v5.push(&id('z')); //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| HELP consider using a `let` binding to create a longer lived value diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr index a236dab3ae562..2dc29a78d204d 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr @@ -16,7 +16,7 @@ error[E0716]: temporary value dropped while borrowed LL | v3.push(&id('x')); // statement 6 | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here @@ -33,7 +33,7 @@ error[E0716]: temporary value dropped while borrowed LL | v4.push(&id('y')); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | v4.use_ref(); | ------------ borrow later used here @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed LL | v5.push(&id('z')); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.stderr index cb5289d24b4fc..25e344fedfb25 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/borrowck-ref-into-rvalue.rs:4:11 | LL | match Some("Hello".to_string()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/span/issue-15480.stderr b/src/test/ui/span/issue-15480.stderr index 460ad9ac74445..d9cce2254dd95 100644 --- a/src/test/ui/span/issue-15480.stderr +++ b/src/test/ui/span/issue-15480.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/issue-15480.rs:6:10 | LL | &id(3) - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use LL | ]; | - temporary value is freed at the end of this statement ... diff --git a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr index ba0c45acf237d..81e858fa0ce01 100644 --- a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr +++ b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/regions-close-over-borrowed-ref-in-obj.rs:12:27 | LL | let ss: &isize = &id(1); - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/span/slice-borrow.stderr b/src/test/ui/span/slice-borrow.stderr index 27df25be3fa03..b70bf69d688a5 100644 --- a/src/test/ui/span/slice-borrow.stderr +++ b/src/test/ui/span/slice-borrow.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/slice-borrow.rs:6:28 | LL | let x: &[isize] = &vec![1, 2, 3, 4, 5]; - | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/static/static-drop-scope.stderr b/src/test/ui/static/static-drop-scope.stderr index 112bfc003048d..cedcb7367949f 100644 --- a/src/test/ui/static/static-drop-scope.stderr +++ b/src/test/ui/static/static-drop-scope.stderr @@ -13,7 +13,7 @@ LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor); | ------^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0493]: destructor of `WithDtor` cannot be evaluated at compile-time @@ -31,7 +31,7 @@ LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor); | ------^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0493]: destructor of `(WithDtor, i32)` cannot be evaluated at compile-time diff --git a/src/test/ui/static/static-reference-to-fn-2.stderr b/src/test/ui/static/static-reference-to-fn-2.stderr index ff15884bd445d..133d8ec2e1e51 100644 --- a/src/test/ui/static/static-reference-to-fn-2.stderr +++ b/src/test/ui/static/static-reference-to-fn-2.stderr @@ -6,7 +6,7 @@ LL | fn state1(self_: &mut StateMachineIter) -> Option<&'static str> { LL | self_.statefn = &id(state2 as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0716]: temporary value dropped while borrowed @@ -17,7 +17,7 @@ LL | fn state2(self_: &mut StateMachineIter) -> Option<(&'static str)> { LL | self_.statefn = &id(state3 as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0716]: temporary value dropped while borrowed @@ -28,7 +28,7 @@ LL | fn state3(self_: &mut StateMachineIter) -> Option<(&'static str)> { LL | self_.statefn = &id(finished as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0515]: cannot return value referencing temporary value diff --git a/src/test/ui/static/static-region-bound.stderr b/src/test/ui/static/static-region-bound.stderr index 15261259ed412..1a607e3c014a3 100644 --- a/src/test/ui/static/static-region-bound.stderr +++ b/src/test/ui/static/static-region-bound.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/static-region-bound.rs:10:14 | LL | let x = &id(3); - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use LL | f(x); | ---- argument requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/statics/issue-44373.stderr b/src/test/ui/statics/issue-44373.stderr index 6f92fbb1eb689..2d29dec888e8d 100644 --- a/src/test/ui/statics/issue-44373.stderr +++ b/src/test/ui/statics/issue-44373.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/issue-44373.rs:4:42 | LL | let _val: &'static [&'static u32] = &[&FOO]; - | ----------------------- ^^^^^^ creates a temporary which is freed while still in use + | ----------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } From 059e52b96b0127a4b6e7f566a2023482e34c27f6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Oct 2022 17:51:48 +0200 Subject: [PATCH 0042/1080] rustc_hir_typeck: fix clippy --- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/lib.rs | 1 + clippy_lints/src/loops/mut_range_bound.rs | 4 ++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/operators/assign_op_pattern.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 7 +++---- clippy_utils/src/usage.rs | 7 +++---- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index eb0455ae404c1..c9a8307eba4f2 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -178,7 +178,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dcefd78763b7..89ffca8128a9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index db73ab55b37cf..91b321c447479 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,7 +4,7 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; @@ -115,7 +115,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6017941452c06..5cf88bfc8880d 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,7 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; +use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7f881e27dd27c..9c949a28f44d5 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,7 +12,7 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; -use rustc_hir_analysis::expr_use_visitor as euv; +use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -342,7 +342,7 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index c7e964cf23e2c..ee9fd94064c08 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -8,7 +8,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 102f7541c8ce1..70d166c4854c4 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,5 +1,5 @@ use rustc_hir::Expr; -use rustc_hir_analysis::check::{cast, FnCtxt, Inherited}; +use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e7e3625c078ab..7e42fcc6569b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -24,7 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; -extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3347342e412b6..5089987ef7200 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,7 +10,7 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -1054,11 +1054,10 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } #[cfg(test)] diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index e32bae6ed1fd4..000fb51c01857 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -75,11 +75,10 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } pub struct ParamBindingIdCollector { From aa9837ba29d88d0748e2599e46403f333a629836 Mon Sep 17 00:00:00 2001 From: Andrew Tribick Date: Thu, 20 Oct 2022 22:09:24 +0200 Subject: [PATCH 0043/1080] Add tests for rounding of ties during float formatting --- library/core/tests/fmt/float.rs | 120 ++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/library/core/tests/fmt/float.rs b/library/core/tests/fmt/float.rs index bc0bef6d77604..003782f34dc93 100644 --- a/library/core/tests/fmt/float.rs +++ b/library/core/tests/fmt/float.rs @@ -24,6 +24,66 @@ fn test_format_f64() { assert_eq!("1234.6", format!("{:.1?}", 1234.56789f64)); } +#[test] +fn test_format_f64_rounds_ties_to_even() { + assert_eq!("0", format!("{:.0}", 0.5f64)); + assert_eq!("2", format!("{:.0}", 1.5f64)); + assert_eq!("2", format!("{:.0}", 2.5f64)); + assert_eq!("4", format!("{:.0}", 3.5f64)); + assert_eq!("4", format!("{:.0}", 4.5f64)); + assert_eq!("6", format!("{:.0}", 5.5f64)); + assert_eq!("128", format!("{:.0}", 127.5f64)); + assert_eq!("128", format!("{:.0}", 128.5f64)); + assert_eq!("0.2", format!("{:.1}", 0.25f64)); + assert_eq!("0.8", format!("{:.1}", 0.75f64)); + assert_eq!("0.12", format!("{:.2}", 0.125f64)); + assert_eq!("0.88", format!("{:.2}", 0.875f64)); + assert_eq!("0.062", format!("{:.3}", 0.062f64)); + assert_eq!("-0", format!("{:.0}", -0.5f64)); + assert_eq!("-2", format!("{:.0}", -1.5f64)); + assert_eq!("-2", format!("{:.0}", -2.5f64)); + assert_eq!("-4", format!("{:.0}", -3.5f64)); + assert_eq!("-4", format!("{:.0}", -4.5f64)); + assert_eq!("-6", format!("{:.0}", -5.5f64)); + assert_eq!("-128", format!("{:.0}", -127.5f64)); + assert_eq!("-128", format!("{:.0}", -128.5f64)); + assert_eq!("-0.2", format!("{:.1}", -0.25f64)); + assert_eq!("-0.8", format!("{:.1}", -0.75f64)); + assert_eq!("-0.12", format!("{:.2}", -0.125f64)); + assert_eq!("-0.88", format!("{:.2}", -0.875f64)); + assert_eq!("-0.062", format!("{:.3}", -0.062f64)); + + assert_eq!("2e0", format!("{:.0e}", 1.5f64)); + assert_eq!("2e0", format!("{:.0e}", 2.5f64)); + assert_eq!("4e0", format!("{:.0e}", 3.5f64)); + assert_eq!("4e0", format!("{:.0e}", 4.5f64)); + assert_eq!("6e0", format!("{:.0e}", 5.5f64)); + assert_eq!("1.28e2", format!("{:.2e}", 127.5f64)); + assert_eq!("1.28e2", format!("{:.2e}", 128.5f64)); + assert_eq!("-2e0", format!("{:.0e}", -1.5f64)); + assert_eq!("-2e0", format!("{:.0e}", -2.5f64)); + assert_eq!("-4e0", format!("{:.0e}", -3.5f64)); + assert_eq!("-4e0", format!("{:.0e}", -4.5f64)); + assert_eq!("-6e0", format!("{:.0e}", -5.5f64)); + assert_eq!("-1.28e2", format!("{:.2e}", -127.5f64)); + assert_eq!("-1.28e2", format!("{:.2e}", -128.5f64)); + + assert_eq!("2E0", format!("{:.0E}", 1.5f64)); + assert_eq!("2E0", format!("{:.0E}", 2.5f64)); + assert_eq!("4E0", format!("{:.0E}", 3.5f64)); + assert_eq!("4E0", format!("{:.0E}", 4.5f64)); + assert_eq!("6E0", format!("{:.0E}", 5.5f64)); + assert_eq!("1.28E2", format!("{:.2E}", 127.5f64)); + assert_eq!("1.28E2", format!("{:.2E}", 128.5f64)); + assert_eq!("-2E0", format!("{:.0E}", -1.5f64)); + assert_eq!("-2E0", format!("{:.0E}", -2.5f64)); + assert_eq!("-4E0", format!("{:.0E}", -3.5f64)); + assert_eq!("-4E0", format!("{:.0E}", -4.5f64)); + assert_eq!("-6E0", format!("{:.0E}", -5.5f64)); + assert_eq!("-1.28E2", format!("{:.2E}", -127.5f64)); + assert_eq!("-1.28E2", format!("{:.2E}", -128.5f64)); +} + #[test] fn test_format_f32() { assert_eq!("1", format!("{:.0}", 1.0f32)); @@ -50,6 +110,66 @@ fn test_format_f32() { assert_eq!("1234.6", format!("{:.1?}", 1234.56789f32)); } +#[test] +fn test_format_f32_rounds_ties_to_even() { + assert_eq!("0", format!("{:.0}", 0.5f32)); + assert_eq!("2", format!("{:.0}", 1.5f32)); + assert_eq!("2", format!("{:.0}", 2.5f32)); + assert_eq!("4", format!("{:.0}", 3.5f32)); + assert_eq!("4", format!("{:.0}", 4.5f32)); + assert_eq!("6", format!("{:.0}", 5.5f32)); + assert_eq!("128", format!("{:.0}", 127.5f32)); + assert_eq!("128", format!("{:.0}", 128.5f32)); + assert_eq!("0.2", format!("{:.1}", 0.25f32)); + assert_eq!("0.8", format!("{:.1}", 0.75f32)); + assert_eq!("0.12", format!("{:.2}", 0.125f32)); + assert_eq!("0.88", format!("{:.2}", 0.875f32)); + assert_eq!("0.062", format!("{:.3}", 0.062f32)); + assert_eq!("-0", format!("{:.0}", -0.5f32)); + assert_eq!("-2", format!("{:.0}", -1.5f32)); + assert_eq!("-2", format!("{:.0}", -2.5f32)); + assert_eq!("-4", format!("{:.0}", -3.5f32)); + assert_eq!("-4", format!("{:.0}", -4.5f32)); + assert_eq!("-6", format!("{:.0}", -5.5f32)); + assert_eq!("-128", format!("{:.0}", -127.5f32)); + assert_eq!("-128", format!("{:.0}", -128.5f32)); + assert_eq!("-0.2", format!("{:.1}", -0.25f32)); + assert_eq!("-0.8", format!("{:.1}", -0.75f32)); + assert_eq!("-0.12", format!("{:.2}", -0.125f32)); + assert_eq!("-0.88", format!("{:.2}", -0.875f32)); + assert_eq!("-0.062", format!("{:.3}", -0.062f32)); + + assert_eq!("2e0", format!("{:.0e}", 1.5f32)); + assert_eq!("2e0", format!("{:.0e}", 2.5f32)); + assert_eq!("4e0", format!("{:.0e}", 3.5f32)); + assert_eq!("4e0", format!("{:.0e}", 4.5f32)); + assert_eq!("6e0", format!("{:.0e}", 5.5f32)); + assert_eq!("1.28e2", format!("{:.2e}", 127.5f32)); + assert_eq!("1.28e2", format!("{:.2e}", 128.5f32)); + assert_eq!("-2e0", format!("{:.0e}", -1.5f32)); + assert_eq!("-2e0", format!("{:.0e}", -2.5f32)); + assert_eq!("-4e0", format!("{:.0e}", -3.5f32)); + assert_eq!("-4e0", format!("{:.0e}", -4.5f32)); + assert_eq!("-6e0", format!("{:.0e}", -5.5f32)); + assert_eq!("-1.28e2", format!("{:.2e}", -127.5f32)); + assert_eq!("-1.28e2", format!("{:.2e}", -128.5f32)); + + assert_eq!("2E0", format!("{:.0E}", 1.5f32)); + assert_eq!("2E0", format!("{:.0E}", 2.5f32)); + assert_eq!("4E0", format!("{:.0E}", 3.5f32)); + assert_eq!("4E0", format!("{:.0E}", 4.5f32)); + assert_eq!("6E0", format!("{:.0E}", 5.5f32)); + assert_eq!("1.28E2", format!("{:.2E}", 127.5f32)); + assert_eq!("1.28E2", format!("{:.2E}", 128.5f32)); + assert_eq!("-2E0", format!("{:.0E}", -1.5f32)); + assert_eq!("-2E0", format!("{:.0E}", -2.5f32)); + assert_eq!("-4E0", format!("{:.0E}", -3.5f32)); + assert_eq!("-4E0", format!("{:.0E}", -4.5f32)); + assert_eq!("-6E0", format!("{:.0E}", -5.5f32)); + assert_eq!("-1.28E2", format!("{:.2E}", -127.5f32)); + assert_eq!("-1.28E2", format!("{:.2E}", -128.5f32)); +} + fn is_exponential(s: &str) -> bool { s.contains("e") || s.contains("E") } From 655175494574265257f52f4f7a1e7eabd85cbe77 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 15:27:25 +0200 Subject: [PATCH 0044/1080] [`unwrap_used`], [`expect_used`] do not lint in `test` cfg --- clippy_lints/src/methods/expect_used.rs | 4 ++-- clippy_lints/src/methods/unwrap_used.rs | 4 ++-- clippy_lints/src/utils/conf.rs | 4 ++-- tests/ui-toml/expect_used/expect_used.rs | 23 ++++++++++++-------- tests/ui-toml/unwrap_used/unwrap_used.rs | 21 ++++++++++++++---- tests/ui-toml/unwrap_used/unwrap_used.stderr | 6 ++--- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc0e..0d3c89280465b 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -27,7 +27,7 @@ pub(super) fn check( let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889ee..d11830a24b6c0 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -27,7 +27,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) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e39..199dfafebf35a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,11 +373,11 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed in test cfg (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 22dcd3ae9d697..bff97d97df77a 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -16,14 +16,19 @@ fn main() { expect_result(); } -#[test] -fn test_expect_option() { - let opt = Some(0); - let _ = opt.expect(""); -} +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); -#[test] -fn test_expect_result() { - let res: Result = Ok(0); - let _ = res.expect(""); + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 0e82fb20e4558..bc8e8c1f0703a 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,8 +66,21 @@ fn main() { } } -#[test] -fn test() { - let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let _ = boxed_slice.get(1).unwrap(); +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); + + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + // should still warn + let _ = Box::new([0]).get(1).unwrap(); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 681b5eaf54dba..2bca88660e1c6 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -188,10 +188,10 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:72:13 + --> $DIR/unwrap_used.rs:84:17 | -LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` +LL | let _ = Box::new([0]).get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` error: aborting due to 27 previous errors From e4ef0e5df9a3aeee2b6af8b4c2a567e7d84cc4b1 Mon Sep 17 00:00:00 2001 From: Justin Mott Date: Fri, 21 Oct 2022 13:28:59 -0400 Subject: [PATCH 0045/1080] addressed https://github.com/rust-lang/rust-analyzer/issues/12536 --- .../ide-assists/src/handlers/inline_call.rs | 86 ++++++++++++++++--- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 9f51cdaf8b1eb..c546ee45d964f 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use ast::make; use either::Either; use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo}; @@ -373,8 +375,44 @@ fn inline( }) } } + + let mut func_let_vars: BTreeSet = BTreeSet::new(); + + // grab all of the local variable declarations in the function + for stmt in fn_body.statements() { + if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) { + for has_token in let_stmt.syntax().children_with_tokens() { + if let Some(node) = has_token.as_node() { + if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) { + func_let_vars.insert(ident_pat.syntax().text().to_string()); + } + } + } + } + } + // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() { + // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors + let usages: &[ast::PathExpr] = &*usages; + let expr: &ast::Expr = expr; + + let insert_let_stmt = || { + let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); + if let Some(stmt_list) = body.stmt_list() { + stmt_list.push_front( + make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), + ) + } + }; + + // check if there is a local var in the function that conflicts with parameter + // if it does then emit a let statement and continue + if func_let_vars.contains(&expr.syntax().text().to_string()) { + insert_let_stmt(); + continue; + } + let inline_direct = |usage, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); @@ -383,9 +421,7 @@ fn inline( ted::replace(usage.syntax(), &replacement.syntax().clone_for_update()); } }; - // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors - let usages: &[ast::PathExpr] = &*usages; - let expr: &ast::Expr = expr; + match usages { // inline single use closure arguments [usage] @@ -408,18 +444,11 @@ fn inline( } // can't inline, emit a let statement _ => { - let ty = - sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); - if let Some(stmt_list) = body.stmt_list() { - stmt_list.push_front( - make::let_stmt(pat.clone(), ty, Some(expr.clone())) - .clone_for_update() - .into(), - ) - } + insert_let_stmt(); } } } + if let Some(generic_arg_list) = generic_arg_list.clone() { if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { @@ -1256,4 +1285,37 @@ impl A { "#, ) } + + #[test] + fn local_variable_shadowing_callers_argument() { + check_assist( + inline_call, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = foo$0(a, b); +} +"#, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = { + let bar = a; + let a = 1; + bar * b * a * 6 + }; +} +"#, + ); + } } From a3c3f722b7e71d5c9985ba318c700b697fd2c106 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 22 Oct 2022 03:03:48 +0300 Subject: [PATCH 0046/1080] Fix mod_inv termination for the last iteration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On usize=u64 platforms, the 4th iteration would overflow the `mod_gate` back to 0. Similarly for usize=u32 platforms, the 3rd iteration would overflow much the same way. I tested various approaches to resolving this, including approaches with `saturating_mul` and `widening_mul` to a double usize. Turns out LLVM likes `mul_with_overflow` the best. In fact now, that LLVM can see the iteration count is limited, it will happily unroll the loop into a nice linear sequence. You will also notice that the code around the loop got simplified somewhat. Now that LLVM is handling the loop nicely, there isn’t any more reasons to manually unroll the first iteration out of the loop (though looking at the code today I’m not sure all that complexity was necessary in the first place). Fixes #103361 --- library/core/src/ptr/mod.rs | 54 +++++++++++++++++++------------------ library/core/tests/ptr.rs | 12 +++++++++ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8e2bad35993df..8a9bcf38b906c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1571,8 +1571,8 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ - cttz_nonzero, exact_div, unchecked_rem, unchecked_shl, unchecked_shr, unchecked_sub, - wrapping_add, wrapping_mul, wrapping_sub, + cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl, unchecked_shr, + unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, }; /// Calculate multiplicative modular inverse of `x` modulo `m`. @@ -1592,36 +1592,38 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { const INV_TABLE_MOD_16: [u8; 8] = [1, 11, 13, 7, 9, 3, 5, 15]; /// Modulo for which the `INV_TABLE_MOD_16` is intended. const INV_TABLE_MOD: usize = 16; - /// INV_TABLE_MOD² - const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD; - let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; // SAFETY: `m` is required to be a power-of-two, hence non-zero. let m_minus_one = unsafe { unchecked_sub(m, 1) }; - if m <= INV_TABLE_MOD { - table_inverse & m_minus_one - } else { - // We iterate "up" using the following formula: - // - // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ + let mut inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; + let mut mod_gate = INV_TABLE_MOD; + // We iterate "up" using the following formula: + // + // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ + // + // This application needs to be applied at least until `2²ⁿ ≥ m`, at which point we can + // finally reduce the computation to our desired `m` by taking `inverse mod m`. + // + // This computation is `O(log log m)`, which is to say, that on 64-bit machines this loop + // will always finish in at most 4 iterations. + loop { + // y = y * (2 - xy) mod n // - // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`. - let mut inverse = table_inverse; - let mut going_mod = INV_TABLE_MOD_SQUARED; - loop { - // y = y * (2 - xy) mod n - // - // Note, that we use wrapping operations here intentionally – the original formula - // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod - // usize::MAX` instead, because we take the result `mod n` at the end - // anyway. - inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); - if going_mod >= m { - return inverse & m_minus_one; - } - going_mod = wrapping_mul(going_mod, going_mod); + // Note, that we use wrapping operations here intentionally – the original formula + // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod + // usize::MAX` instead, because we take the result `mod n` at the end + // anyway. + if mod_gate >= m { + break; + } + inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); + let (new_gate, overflow) = mul_with_overflow(mod_gate, mod_gate); + if overflow { + break; } + mod_gate = new_gate; } + inverse & m_minus_one } let addr = p.addr(); diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 97a369810056d..0977980ba47bf 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -455,6 +455,18 @@ fn align_offset_various_strides() { assert!(!x); } +#[test] +fn align_offset_issue_103361() { + #[cfg(target_pointer_width = "64")] + const SIZE: usize = 1 << 47; + #[cfg(target_pointer_width = "32")] + const SIZE: usize = 1 << 30; + #[cfg(target_pointer_width = "16")] + const SIZE: usize = 1 << 13; + struct HugeSize([u8; SIZE - 1]); + let _ = (SIZE as *const HugeSize).align_offset(SIZE); +} + #[test] fn offset_from() { let mut a = [0; 5]; From 83332539dd19f61893846e907d55c9abd613d007 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Oct 2022 06:43:51 +0000 Subject: [PATCH 0047/1080] Check fat pointer metadata compatibility modulo regions --- compiler/rustc_hir_typeck/src/cast.rs | 13 +++++++------ src/test/ui/cast/cast-pointee-projection.rs | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/cast/cast-pointee-projection.rs diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 0e7576ecf8b0c..0757a2d6d9a38 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -33,6 +33,7 @@ use super::FnCtxt; use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; @@ -65,7 +66,7 @@ pub struct CastCheck<'tcx> { /// The kind of pointer and associated metadata (thin, length or vtable) - we /// only allow casts between fat pointers if their metadata have the same /// kind. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, @@ -74,11 +75,11 @@ enum PointerKind<'tcx> { /// Slice Length, /// The unsize info of this projection - OfProjection(&'tcx ty::ProjectionTy<'tcx>), + OfProjection(ty::ProjectionTy<'tcx>), /// The unsize info of this opaque ty OfOpaque(DefId, SubstsRef<'tcx>), /// The unsize info of this parameter - OfParam(&'tcx ty::ParamTy), + OfParam(ty::ParamTy), } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -119,9 +120,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pointers to foreign types are thin, despite being unsized ty::Foreign(..) => Some(PointerKind::Thin), // We should really try to normalize here. - ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::Projection(pi) => Some(PointerKind::OfProjection(pi)), ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), - ty::Param(ref p) => Some(PointerKind::OfParam(p)), + ty::Param(p) => Some(PointerKind::OfParam(p)), // Insufficient type information. ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, @@ -903,7 +904,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } // vtable kinds must match - if cast_kind == expr_kind { + if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { Ok(CastKind::PtrPtrCast) } else { Err(CastError::DifferingKinds) diff --git a/src/test/ui/cast/cast-pointee-projection.rs b/src/test/ui/cast/cast-pointee-projection.rs new file mode 100644 index 0000000000000..f51c5f20f167c --- /dev/null +++ b/src/test/ui/cast/cast-pointee-projection.rs @@ -0,0 +1,17 @@ +// check-pass + +trait Tag<'a> { + type Type: ?Sized; +} + +trait IntoRaw: for<'a> Tag<'a> { + fn into_raw(this: *const >::Type) -> *mut >::Type; +} + +impl Tag<'a>> IntoRaw for T { + fn into_raw(this: *const >::Type) -> *mut >::Type { + this as *mut T::Type + } +} + +fn main() {} From 2ed404937f2e9fcd481c717ef4d386d053ea59e3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Oct 2022 18:48:28 +0000 Subject: [PATCH 0048/1080] Introduce subst_iter and subst_iter_copied on EarlyBinder --- clippy_utils/src/ty.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a15daec7c3ce3..3b5a9ba83568c 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -657,21 +657,18 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O let mut output = None; let lang_items = cx.tcx.lang_items(); - for pred in cx + for (pred, _) in cx .tcx .bound_explicit_item_bounds(ty.item_def_id) - .transpose_iter() - .map(|x| x.map_bound(|(p, _)| p)) + .subst_iter_copied(cx.tcx, ty.substs) { - match pred.0.kind().skip_binder() { + match pred.kind().skip_binder() { PredicateKind::Trait(p) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => { - let i = pred - .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1))) - .subst(cx.tcx, ty.substs); + let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); if inputs.map_or(false, |inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? @@ -684,10 +681,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O // Multiple different fn trait impls. Is this even allowed? return None; } - output = Some( - pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap())) - .subst(cx.tcx, ty.substs), - ); + output = pred.kind().rebind(p.term.ty()).transpose(); }, _ => (), } From 964290a0ada338f129a0ebd006e6cd3e1f55951f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Oct 2022 11:04:54 +0200 Subject: [PATCH 0049/1080] Pin::new_unchecked: discuss pinning closure captures --- library/core/src/pin.rs | 57 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index ccef35b45325a..5d487a5be2b65 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -543,7 +543,7 @@ impl Pin

{ /// let p: Pin<&mut T> = Pin::new_unchecked(&mut a); /// // This should mean the pointee `a` can never move again. /// } - /// mem::swap(&mut a, &mut b); + /// mem::swap(&mut a, &mut b); // Potential UB down the road ⚠️ /// // The address of `a` changed to `b`'s stack slot, so `a` got moved even /// // though we have previously pinned it! We have violated the pinning API contract. /// } @@ -563,13 +563,66 @@ impl Pin

{ /// // This should mean the pointee can never move again. /// } /// drop(pinned); - /// let content = Rc::get_mut(&mut x).unwrap(); + /// let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️ /// // Now, if `x` was the only reference, we have a mutable reference to /// // data that we pinned above, which we could use to move it as we have /// // seen in the previous example. We have violated the pinning API contract. /// } /// ``` /// + /// ## Pinning of closure captures + /// + /// Particular care is required when using `Pin::new_unchecked` in a closure: + /// `Pin::new_unchecked(&mut var)` where `var` is a by-value (moved) closure capture + /// implicitly makes the promise that the closure itself is pinned, and that *all* uses + /// of this closure capture respect that pinning. + /// ``` + /// use std::pin::Pin; + /// use std::task::Context; + /// use std::future::Future; + /// + /// fn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) { + /// // Create a closure that moves `x`, and then internally uses it in a pinned way. + /// let mut closure = move || unsafe { + /// let _ignore = Pin::new_unchecked(&mut x).poll(cx); + /// }; + /// // Call the closure, so the future can assume it has been pinned. + /// closure(); + /// // Move the closure somewhere else. This also moves `x`! + /// let mut moved = closure; + /// // Calling it again means we polled the future from two different locations, + /// // violating the pinning API contract. + /// moved(); // Potential UB ⚠️ + /// } + /// ``` + /// When passing a closure to another API, it might be moving the closure any time, so + /// `Pin::new_unchecked` on closure captures may only be used if the API explicitly documents + /// that the closure is pinned. + /// + /// The better alternative is to avoid all that trouble and do the pinning in the outer function + /// instead (here using the unstable `pin` macro): + /// ``` + /// #![feature(pin_macro)] + /// use std::pin::pin; + /// use std::task::Context; + /// use std::future::Future; + /// + /// fn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) { + /// let mut x = pin!(x); + /// // Create a closure that captures `x: Pin<&mut _>`, which is safe to move. + /// let mut closure = move || { + /// let _ignore = x.as_mut().poll(cx); + /// }; + /// // Call the closure, so the future can assume it has been pinned. + /// closure(); + /// // Move the closure somewhere else. + /// let mut moved = closure; + /// // Calling it again here is fine (except that we might be polling a future that already + /// // returned `Poll::Ready`, but that is a separate problem). + /// moved(); + /// } + /// ``` + /// /// [`mem::swap`]: crate::mem::swap #[lang = "new_unchecked"] #[inline(always)] From 8039a07a5e13fa0690b56f3c6074e5581a4fd4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 22 Oct 2022 15:10:03 +0200 Subject: [PATCH 0050/1080] ide: Generate monikers for local crates. --- crates/ide/src/moniker.rs | 28 +++++++++---------- crates/rust-analyzer/src/cli/lsif.rs | 6 ++--- crates/rust-analyzer/src/cli/scip.rs | 40 +++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 852a8fd837616..07d117aff10bd 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -73,8 +73,8 @@ impl MonikerResult { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, - pub repo: String, - pub version: String, + pub repo: Option, + pub version: Option, } pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { @@ -256,18 +256,18 @@ pub(crate) fn def_to_moniker( let (name, repo, version) = match krate.origin(db) { CrateOrigin::CratesIo { repo, name } => ( name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()), - repo?, - krate.version(db)?, + repo, + krate.version(db), ), CrateOrigin::Lang(lang) => ( krate.display_name(db)?.canonical_name().to_string(), - "https://github.com/rust-lang/rust/".to_string(), - match lang { + Some("https://github.com/rust-lang/rust/".to_string()), + Some(match lang { LangCrateOrigin::Other => { "https://github.com/rust-lang/rust/library/".into() } lang => format!("https://github.com/rust-lang/rust/library/{lang}",), - }, + }), ), }; PackageInformation { name, repo, version } @@ -315,7 +315,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); check_moniker( @@ -331,7 +331,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -348,7 +348,7 @@ pub mod module { } "#, "foo::module::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -365,7 +365,7 @@ pub mod module { } "#, "foo::module::MyTrait::MY_CONST", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -382,7 +382,7 @@ pub mod module { } "#, "foo::module::MyTrait::MyType", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -405,7 +405,7 @@ pub mod module { } "#, "foo::module::MyStruct::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -425,7 +425,7 @@ pub struct St { } "#, "foo::St::a", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); } diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 748306ea57d4e..76abe65898075 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -106,12 +106,12 @@ impl LsifManager<'_> { manager: "cargo".to_string(), uri: None, content: None, - repository: Some(lsif::Repository { - url: pi.repo, + repository: pi.repo.map(|url| lsif::Repository { + url, r#type: "git".to_string(), commit_id: None, }), - version: Some(pi.version), + version: pi.version, })); self.package_map.insert(package_information, result_set_id); result_set_id diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 8b77ccde0ee4a..b03f085d692e6 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -231,7 +231,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { package: Some(scip_types::Package { manager: "cargo".to_string(), name: package_name, - version, + version: version.unwrap_or_else(|| ".".to_string()), ..Default::default() }) .into(), @@ -415,4 +415,42 @@ pub mod module { "", ); } + + #[test] + fn global_symbol_for_pub_struct() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar { i: 0 }; + } + //- /foo.rs + pub struct Bar$0 { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } + + #[test] + fn global_symbol_for_pub_struct_reference() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar$0 { i: 0 }; + } + //- /foo.rs + pub struct Bar { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } } From a0c82d2bcce1b636c02bb6bf942c05af8dc3ac9a Mon Sep 17 00:00:00 2001 From: kraktus <56031107+kraktus@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:07:05 +0200 Subject: [PATCH 0051/1080] Explicitly mention [cfg(test)] Co-authored-by: llogiq --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 199dfafebf35a..a10de31556c82 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,7 +373,7 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test cfg + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// From cd0bb7de01d6f516c2e69c03f3bed26aaf167bc8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 23 Oct 2022 15:18:45 +0200 Subject: [PATCH 0052/1080] Merge commit '4f142aa1058f14f153f8bfd2d82f04ddb9982388' into clippyup --- .github/workflows/clippy.yml | 1 + .github/workflows/clippy_bors.yml | 1 + .github/workflows/clippy_dev.yml | 2 + CHANGELOG.md | 6 + CONTRIBUTING.md | 2 +- book/src/development/adding_lints.md | 23 +- book/src/development/basics.md | 7 +- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/setup/intellij.rs | 17 +- clippy_dev/src/update_lints.rs | 10 +- clippy_lints/src/booleans.rs | 6 +- clippy_lints/src/box_default.rs | 96 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 38 + clippy_lints/src/casts/cast_nan_to_int.rs | 28 + clippy_lints/src/casts/mod.rs | 57 +- clippy_lints/src/casts/unnecessary_cast.rs | 3 - clippy_lints/src/comparison_chain.rs | 8 +- clippy_lints/src/default_numeric_fallback.rs | 65 +- clippy_lints/src/dereference.rs | 99 +- clippy_lints/src/derive.rs | 5 +- clippy_lints/src/disallowed_methods.rs | 5 +- clippy_lints/src/entry.rs | 20 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/format_args.rs | 165 +- clippy_lints/src/from_over_into.rs | 176 +- clippy_lints/src/functions/must_use.rs | 10 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- .../src/invalid_upcast_comparisons.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 5 +- clippy_lints/src/let_underscore.rs | 23 +- clippy_lints/src/lib.register_all.rs | 5 + clippy_lints/src/lib.register_complexity.rs | 2 + clippy_lints/src/lib.register_internal.rs | 32 +- clippy_lints/src/lib.register_lints.rs | 38 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 2 - clippy_lints/src/lib.register_restriction.rs | 2 + clippy_lints/src/lib.register_style.rs | 2 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 43 +- clippy_lints/src/loops/needless_range_loop.rs | 35 +- .../src/loops/while_let_on_iterator.rs | 5 +- clippy_lints/src/manual_async_fn.rs | 18 +- clippy_lints/src/manual_clamp.rs | 10 +- clippy_lints/src/matches/collapsible_match.rs | 23 +- clippy_lints/src/matches/manual_filter.rs | 153 ++ clippy_lints/src/matches/manual_map.rs | 318 +--- clippy_lints/src/matches/manual_utils.rs | 277 +++ clippy_lints/src/matches/match_same_arms.rs | 6 +- .../src/matches/match_single_binding.rs | 31 +- clippy_lints/src/matches/mod.rs | 40 + clippy_lints/src/matches/single_match.rs | 11 +- clippy_lints/src/methods/into_iter_on_ref.rs | 5 +- .../methods/manual_saturating_arithmetic.rs | 10 +- clippy_lints/src/methods/mod.rs | 25 +- .../src/methods/option_as_ref_deref.rs | 20 +- clippy_lints/src/methods/or_fun_call.rs | 23 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/misc_early/literal_suffix.rs | 4 +- .../src/misc_early/mixed_case_hex_literals.rs | 4 +- .../src/misc_early/zero_prefixed_literal.rs | 18 +- .../src/mismatching_type_param_order.rs | 5 +- clippy_lints/src/missing_trait_methods.rs | 98 + .../src/mixed_read_write_in_expression.rs | 5 +- clippy_lints/src/needless_for_each.rs | 5 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 8 +- clippy_lints/src/non_copy_const.rs | 5 +- .../src/non_octal_unix_permissions.rs | 17 +- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/operators/cmp_owned.rs | 18 +- clippy_lints/src/partial_pub_fields.rs | 81 + clippy_lints/src/ptr.rs | 54 +- clippy_lints/src/ptr_offset_with_cast.rs | 10 +- clippy_lints/src/redundant_clone.rs | 454 +---- clippy_lints/src/ref_option_ref.rs | 3 +- clippy_lints/src/shadow.rs | 5 +- clippy_lints/src/transmute/utils.rs | 24 +- clippy_lints/src/types/rc_buffer.rs | 23 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/unit_return_expecting_ord.rs | 17 +- clippy_lints/src/unnested_or_patterns.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 5 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/author.rs | 117 +- clippy_lints/src/utils/internal_lints.rs | 1576 +---------------- .../internal_lints/clippy_lints_internal.rs | 49 + .../utils/internal_lints/collapsible_calls.rs | 245 +++ .../internal_lints/compiler_lint_functions.rs | 77 + .../utils/internal_lints/if_chain_style.rs | 164 ++ .../interning_defined_symbol.rs | 239 +++ .../src/utils/internal_lints/invalid_paths.rs | 108 ++ .../internal_lints/lint_without_lint_pass.rs | 342 ++++ .../internal_lints/metadata_collector.rs | 8 +- .../utils/internal_lints/msrv_attr_impl.rs | 63 + .../internal_lints/outer_expn_data_pass.rs | 62 + .../src/utils/internal_lints/produce_ice.rs | 37 + .../internal_lints/unnecessary_def_path.rs | 343 ++++ clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/consts.rs | 44 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/lib.rs | 48 +- clippy_utils/src/macros.rs | 43 +- clippy_utils/src/mir/maybe_storage_live.rs | 52 + clippy_utils/src/mir/mod.rs | 164 ++ clippy_utils/src/mir/possible_borrower.rs | 241 +++ clippy_utils/src/mir/possible_origin.rs | 59 + clippy_utils/src/mir/transitive_relation.rs | 29 + clippy_utils/src/numeric_literal.rs | 7 +- clippy_utils/src/paths.rs | 36 - clippy_utils/src/sugg.rs | 13 +- lintcheck/src/config.rs | 5 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/main.rs | 162 +- lintcheck/src/recursive.rs | 8 +- rust-toolchain | 2 +- src/docs.rs | 6 + src/docs/as_ptr_cast_mut.txt | 19 + src/docs/box_default.txt | 6 - src/docs/cast_nan_to_int.txt | 15 + src/docs/manual_filter.txt | 21 + src/docs/missing_trait_methods.txt | 40 + src/docs/partial_pub_fields.txt | 27 + src/docs/unused_format_specs.txt | 24 + tests/compile-test.rs | 2 +- tests/dogfood.rs | 9 +- tests/ui-internal/custom_ice_message.stderr | 2 +- tests/ui-internal/invalid_paths.rs | 2 +- tests/ui-internal/unnecessary_def_path.fixed | 6 +- tests/ui-internal/unnecessary_def_path.rs | 6 +- .../unnecessary_def_path_hardcoded_path.rs | 16 + ...unnecessary_def_path_hardcoded_path.stderr | 27 + tests/ui/as_ptr_cast_mut.rs | 37 + tests/ui/as_ptr_cast_mut.stderr | 16 + tests/ui/author.stdout | 24 +- tests/ui/author/blocks.stdout | 116 +- tests/ui/author/call.stdout | 28 +- tests/ui/author/if.stdout | 92 +- tests/ui/author/issue_3849.stdout | 24 +- tests/ui/author/loop.stdout | 202 +-- tests/ui/author/matches.stdout | 72 +- tests/ui/author/repeat.stdout | 20 +- tests/ui/author/struct.stdout | 112 +- tests/ui/box_default.fixed | 57 + tests/ui/box_default.rs | 28 +- tests/ui/box_default.stderr | 83 +- tests/ui/box_default_no_std.rs | 33 + tests/ui/cast_abs_to_unsigned.fixed | 18 +- tests/ui/cast_abs_to_unsigned.rs | 18 +- tests/ui/cast_abs_to_unsigned.stderr | 42 +- tests/ui/cast_lossless_bool.fixed | 13 + tests/ui/cast_lossless_bool.rs | 13 + tests/ui/cast_lossless_bool.stderr | 34 +- tests/ui/cast_nan_to_int.rs | 18 + tests/ui/cast_nan_to_int.stderr | 51 + tests/ui/cfg_attr_rustfmt.fixed | 16 +- tests/ui/cfg_attr_rustfmt.rs | 16 +- tests/ui/cfg_attr_rustfmt.stderr | 8 +- tests/ui/checked_conversions.fixed | 16 + tests/ui/checked_conversions.rs | 16 + tests/ui/checked_conversions.stderr | 40 +- tests/ui/cloned_instead_of_copied.fixed | 24 + tests/ui/cloned_instead_of_copied.rs | 24 + tests/ui/cloned_instead_of_copied.stderr | 30 +- tests/ui/collapsible_match.rs | 21 + tests/ui/collapsible_match.stderr | 34 +- tests/ui/crashes/ice-9625.rs | 4 + tests/ui/default_numeric_fallback_f64.fixed | 9 + tests/ui/default_numeric_fallback_f64.rs | 9 + tests/ui/default_numeric_fallback_f64.stderr | 34 +- tests/ui/default_numeric_fallback_i32.fixed | 10 + tests/ui/default_numeric_fallback_i32.rs | 10 + tests/ui/default_numeric_fallback_i32.stderr | 34 +- tests/ui/err_expect.fixed | 17 + tests/ui/err_expect.rs | 17 + tests/ui/err_expect.stderr | 10 +- tests/ui/filter_map_next_fixable.fixed | 16 + tests/ui/filter_map_next_fixable.rs | 16 + tests/ui/filter_map_next_fixable.stderr | 10 +- tests/ui/format_args.fixed | 3 + tests/ui/format_args.rs | 3 + tests/ui/format_args.stderr | 60 +- tests/ui/from_over_into.fixed | 87 + tests/ui/from_over_into.rs | 66 + tests/ui/from_over_into.stderr | 70 +- tests/ui/from_over_into_unfixable.rs | 35 + tests/ui/from_over_into_unfixable.stderr | 29 + tests/ui/implicit_saturating_sub.fixed | 50 + tests/ui/implicit_saturating_sub.rs | 50 + tests/ui/implicit_saturating_sub.stderr | 46 +- tests/ui/literals.rs | 7 + tests/ui/literals.stderr | 35 +- tests/ui/manual_assert.edition2018.fixed | 39 +- tests/ui/manual_assert.edition2018.stderr | 67 +- tests/ui/manual_assert.edition2021.fixed | 4 +- tests/ui/manual_assert.rs | 4 +- tests/ui/manual_clamp.rs | 27 + tests/ui/manual_clamp.stderr | 85 +- tests/ui/manual_filter.fixed | 119 ++ tests/ui/manual_filter.rs | 243 +++ tests/ui/manual_filter.stderr | 191 ++ tests/ui/manual_rem_euclid.fixed | 30 + tests/ui/manual_rem_euclid.rs | 30 + tests/ui/manual_rem_euclid.stderr | 30 +- tests/ui/manual_strip.rs | 19 + tests/ui/manual_strip.stderr | 47 +- tests/ui/map_unwrap_or.rs | 20 +- tests/ui/map_unwrap_or.stderr | 30 +- tests/ui/match_expr_like_matches_macro.fixed | 16 + tests/ui/match_expr_like_matches_macro.rs | 19 + tests/ui/match_expr_like_matches_macro.stderr | 38 +- tests/ui/match_overlapping_arm.rs | 1 - tests/ui/match_overlapping_arm.stderr | 32 +- tests/ui/match_single_binding.fixed | 9 + tests/ui/match_single_binding.rs | 8 + tests/ui/match_single_binding.stderr | 19 +- .../ui/match_wild_err_arm.edition2021.stderr | 35 - tests/ui/match_wild_err_arm.rs | 3 - ...n2018.stderr => match_wild_err_arm.stderr} | 8 +- tests/ui/mem_replace.fixed | 18 +- tests/ui/mem_replace.rs | 18 +- tests/ui/mem_replace.stderr | 46 +- tests/ui/min_rust_version_attr.rs | 239 +-- tests/ui/min_rust_version_attr.stderr | 46 +- tests/ui/min_rust_version_invalid_attr.rs | 14 + tests/ui/min_rust_version_invalid_attr.stderr | 44 +- .../min_rust_version_multiple_inner_attr.rs | 11 - ...in_rust_version_multiple_inner_attr.stderr | 38 - tests/ui/min_rust_version_no_patch.rs | 14 - tests/ui/min_rust_version_outer_attr.rs | 4 - tests/ui/min_rust_version_outer_attr.stderr | 8 - .../ui/missing_const_for_fn/could_be_const.rs | 12 + .../could_be_const.stderr | 12 +- tests/ui/missing_trait_methods.rs | 50 + tests/ui/missing_trait_methods.stderr | 27 + tests/ui/needless_borrow.fixed | 62 +- tests/ui/needless_borrow.rs | 60 +- tests/ui/needless_borrow.stderr | 96 +- tests/ui/option_as_ref_deref.fixed | 17 +- tests/ui/option_as_ref_deref.rs | 17 +- tests/ui/option_as_ref_deref.stderr | 42 +- tests/ui/or_fun_call.fixed | 11 + tests/ui/or_fun_call.rs | 11 + tests/ui/partial_pub_fields.rs | 40 + tests/ui/partial_pub_fields.stderr | 35 + tests/ui/ptr_arg.rs | 30 +- tests/ui/ptr_arg.stderr | 20 +- tests/ui/range_contains.fixed | 26 +- tests/ui/range_contains.rs | 26 +- tests/ui/range_contains.stderr | 48 +- tests/ui/redundant_field_names.fixed | 16 + tests/ui/redundant_field_names.rs | 16 + tests/ui/redundant_field_names.stderr | 22 +- tests/ui/redundant_static_lifetimes.fixed | 13 + tests/ui/redundant_static_lifetimes.rs | 13 + tests/ui/redundant_static_lifetimes.stderr | 40 +- tests/ui/ref_option_ref.rs | 5 + tests/ui/uninlined_format_args.fixed | 13 + tests/ui/uninlined_format_args.rs | 13 + tests/ui/uninlined_format_args.stderr | 40 +- ...nlined_format_args_panic.edition2018.fixed | 29 + ...lined_format_args_panic.edition2018.stderr | 15 + ...nlined_format_args_panic.edition2021.fixed | 29 + ...lined_format_args_panic.edition2021.stderr | 51 + tests/ui/uninlined_format_args_panic.rs | 29 + tests/ui/unnecessary_cast.fixed | 4 + tests/ui/unnecessary_cast.rs | 4 + tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- tests/ui/unnested_or_patterns.fixed | 16 +- tests/ui/unnested_or_patterns.rs | 16 +- tests/ui/unnested_or_patterns.stderr | 13 +- tests/ui/unused_format_specs.fixed | 18 + tests/ui/unused_format_specs.rs | 18 + tests/ui/unused_format_specs.stderr | 54 + tests/ui/unused_format_specs_unfixable.rs | 30 + tests/ui/unused_format_specs_unfixable.stderr | 69 + tests/ui/use_self.fixed | 33 + tests/ui/use_self.rs | 33 + tests/ui/use_self.stderr | 90 +- tests/versioncheck.rs | 2 +- util/gh-pages/index.html | 6 + util/gh-pages/script.js | 15 +- 284 files changed, 8511 insertions(+), 4206 deletions(-) create mode 100644 clippy_lints/src/casts/as_ptr_cast_mut.rs create mode 100644 clippy_lints/src/casts/cast_nan_to_int.rs create mode 100644 clippy_lints/src/matches/manual_filter.rs create mode 100644 clippy_lints/src/matches/manual_utils.rs create mode 100644 clippy_lints/src/missing_trait_methods.rs create mode 100644 clippy_lints/src/partial_pub_fields.rs create mode 100644 clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs create mode 100644 clippy_lints/src/utils/internal_lints/collapsible_calls.rs create mode 100644 clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs create mode 100644 clippy_lints/src/utils/internal_lints/if_chain_style.rs create mode 100644 clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs create mode 100644 clippy_lints/src/utils/internal_lints/invalid_paths.rs create mode 100644 clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs create mode 100644 clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/produce_ice.rs create mode 100644 clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs create mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 clippy_utils/src/mir/mod.rs create mode 100644 clippy_utils/src/mir/possible_borrower.rs create mode 100644 clippy_utils/src/mir/possible_origin.rs create mode 100644 clippy_utils/src/mir/transitive_relation.rs create mode 100644 src/docs/as_ptr_cast_mut.txt create mode 100644 src/docs/cast_nan_to_int.txt create mode 100644 src/docs/manual_filter.txt create mode 100644 src/docs/missing_trait_methods.txt create mode 100644 src/docs/partial_pub_fields.txt create mode 100644 src/docs/unused_format_specs.txt create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr create mode 100644 tests/ui/as_ptr_cast_mut.rs create mode 100644 tests/ui/as_ptr_cast_mut.stderr create mode 100644 tests/ui/box_default.fixed create mode 100644 tests/ui/box_default_no_std.rs create mode 100644 tests/ui/cast_nan_to_int.rs create mode 100644 tests/ui/cast_nan_to_int.stderr create mode 100644 tests/ui/crashes/ice-9625.rs create mode 100644 tests/ui/from_over_into.fixed create mode 100644 tests/ui/from_over_into_unfixable.rs create mode 100644 tests/ui/from_over_into_unfixable.stderr create mode 100644 tests/ui/manual_filter.fixed create mode 100644 tests/ui/manual_filter.rs create mode 100644 tests/ui/manual_filter.stderr delete mode 100644 tests/ui/match_wild_err_arm.edition2021.stderr rename tests/ui/{match_wild_err_arm.edition2018.stderr => match_wild_err_arm.stderr} (86%) delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr delete mode 100644 tests/ui/min_rust_version_no_patch.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.stderr create mode 100644 tests/ui/missing_trait_methods.rs create mode 100644 tests/ui/missing_trait_methods.stderr create mode 100644 tests/ui/partial_pub_fields.rs create mode 100644 tests/ui/partial_pub_fields.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.stderr create mode 100644 tests/ui/uninlined_format_args_panic.rs create mode 100644 tests/ui/unused_format_specs.fixed create mode 100644 tests/ui/unused_format_specs.rs create mode 100644 tests/ui/unused_format_specs.stderr create mode 100644 tests/ui/unused_format_specs_unfixable.rs create mode 100644 tests/ui/unused_format_specs_unfixable.stderr diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index fac2c99714d9b..b992130119713 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -25,6 +25,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: base: diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 30607af490124..6448b2d4068de 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -11,6 +11,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true defaults: run: diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 22051093c9cf9..14f20212adda5 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -15,6 +15,8 @@ on: env: RUST_BACKTRACE: 1 + CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: clippy_dev: diff --git a/CHANGELOG.md b/CHANGELOG.md index 42615179f7053..2d7bda27e4fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3735,6 +3735,7 @@ Released 2018-09-13 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states @@ -3772,6 +3773,7 @@ Released 2018-09-13 [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless +[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap [`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss @@ -3988,6 +3990,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4046,6 +4049,7 @@ Released 2018-09-13 [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression @@ -4131,6 +4135,7 @@ Released 2018-09-13 [`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_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 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -4312,6 +4317,7 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c977b2cacab5..85f94a74ad91d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## The Clippy book -If you're new to Clippy and don't know where to start the [Clippy book] includes +If you're new to Clippy and don't know where to start, the [Clippy book] includes a [developer guide] and is a good place to start your journey. [Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c8..3c3f368a529b1 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -478,8 +478,27 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +#![feature(custom_inner_attributes)] + +... + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + /* something that would trigger the lint */ +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 44ba6e32755e3..6fb53236e6f1a 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -69,7 +69,7 @@ the reference file with: cargo dev bless ``` -For example, this is necessary, if you fix a typo in an error message of a lint +For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. > _Note:_ This command may update more files than you intended. In that case @@ -101,8 +101,9 @@ cargo dev setup intellij cargo dev dogfood ``` -More about intellij command usage and reasons -[here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust) +More about [intellij] command usage and reasons. + +[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust ## lintcheck diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 2e0794f12fa19..535c25e69f1bb 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -49,7 +49,7 @@ fn mtime(path: impl AsRef) -> SystemTime { .into_iter() .flatten() .flatten() - .map(|entry| mtime(&entry.path())) + .map(|entry| mtime(entry.path())) .max() .unwrap_or(SystemTime::UNIX_EPOCH) } else { diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index b64e79733eb24..efdb158c21e9b 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -36,9 +36,8 @@ impl ClippyProjectInfo { } pub fn setup_rustc_src(rustc_path: &str) { - let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) { - Ok(path) => path, - Err(_) => return, + let Ok(rustc_source_dir) = check_and_get_rustc_dir(rustc_path) else { + return }; for project in CLIPPY_PROJECTS { @@ -172,14 +171,10 @@ pub fn remove_rustc_src() { } fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { - let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) { - content - } else { + let Ok(mut cargo_content) = read_project_file(project.cargo_file) else { return false; }; - let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) { - section_start - } else { + let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) else { println!( "info: dependencies could not be found in `{}` for {}, skipping file", project.cargo_file, project.name @@ -187,9 +182,7 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { return true; }; - let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) { - end_point - } else { + let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) else { eprintln!( "error: the end of the rustc dependencies section could not be found in `{}`", project.cargo_file diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0eb443167ecf3..e690bc369cd43 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -128,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{lint_group}.rs"), + format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -869,13 +869,11 @@ fn clippy_lints_src_files() -> impl Iterator { macro_rules! match_tokens { ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { { - $($(let $capture =)? if let Some(LintDeclSearchResult { + $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { token_kind: TokenKind::$token $({$($fields)*})?, - content: _x, + content: $($capture @)? _, .. - }) = $iter.next() { - _x - } else { + }) = $iter.next() else { continue; };)* #[allow(clippy::unused_unit)] diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2a15cbc7a3c3b..08164c0b654e2 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{eq_expr_value, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -483,7 +483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) + cx.tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) } struct NotSimplificationVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 792183ac40814..36daceabe0bea 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,5 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; -use rustc_hir::{Expr, ExprKind, QPath}; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, + path_def_id, paths, ty::expr_sig, +}; +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_ty, Visitor}, + Block, Expr, ExprKind, Local, Node, QPath, TyKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -15,12 +22,6 @@ declare_clippy_lint! { /// Second, `Box::default()` can be faster /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). /// - /// ### Known problems - /// The lint may miss some cases (e.g. Box::new(String::from(""))). - /// On the other hand, it will trigger on cases where the `default` - /// code comes from a macro that does something different based on - /// e.g. target operating system. - /// /// ### Example /// ```rust /// let x: Box = Box::new(Default::default()); @@ -41,21 +42,88 @@ impl LateLintPass<'_> for BoxDefault { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind - && let ExprKind::Call(..) = arg.kind + && let ExprKind::Call(arg_path, ..) = arg.kind && !in_external_macro(cx.sess(), expr.span) - && expr.span.eq_ctxt(arg.span) + && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new - && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { - span_lint_and_help( + let arg_ty = cx.typeck_results().expr_ty(arg); + span_lint_and_sugg( cx, BOX_DEFAULT, expr.span, "`Box::new(_)` of default value", - None, - "use `Box::default()` instead", + "try", + if is_plain_default(arg_path) || given_type(cx, expr) { + "Box::default()".into() + } else { + format!("Box::<{arg_ty}>::default()") + }, + Applicability::MachineApplicable ); } } } + +fn is_plain_default(arg_path: &Expr<'_>) -> bool { + // we need to match the actual path so we don't match e.g. "u8::default" + if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind { + // avoid generic parameters + match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none()) + } else { + false + } +} + +fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + macro_backtrace(expr.span) + .next() + .map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id)) +} + +#[derive(Default)] +struct InferVisitor(bool); + +impl<'tcx> Visitor<'tcx> for InferVisitor { + fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); + if !self.0 { + walk_ty(self, t); + } + } +} + +fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(Local { ty: Some(ty), .. })) => { + let mut v = InferVisitor::default(); + v.visit_ty(ty); + !v.0 + }, + Some( + Node::Expr(Expr { + kind: ExprKind::Call(path, args), + .. + }) | Node::Block(Block { + expr: + Some(Expr { + kind: ExprKind::Call(path, args), + .. + }), + .. + }), + ) => { + if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && + let Some(sig) = expr_sig(cx, path) && + let Some(input) = sig.input(index) + { + input.no_bound_vars().is_some() + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs new file mode 100644 index 0000000000000..9409f4844f54b --- /dev/null +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::{ + mir::Mutability, + ty::{self, Ty, TypeAndMut}, +}; + +use super::AS_PTR_CAST_MUT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { + if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind() + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = + cx.typeck_results().node_type(cast_expr.hir_id).kind() + && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind + && method_name.ident.name == rustc_span::sym::as_ptr + && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did) + && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() + && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() + && let Some(recv) = snippet_opt(cx, receiver.span) + { + // `as_mut_ptr` might not exist + let applicability = Applicability::MaybeIncorrect; + + span_lint_and_sugg( + cx, + AS_PTR_CAST_MUT, + expr.span, + &format!("casting the result of `as_ptr` to *{ptrty}"), + "replace with", + format!("{recv}.as_mut_ptr()"), + applicability + ); + } +} diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs new file mode 100644 index 0000000000000..322dc41b3a197 --- /dev/null +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -0,0 +1,28 @@ +use super::CAST_NAN_TO_INT; + +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_note; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) { + if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) { + span_lint_and_note( + cx, + CAST_NAN_TO_INT, + expr.span, + &format!("casting a known NaN to {to_ty}"), + None, + "this always evaluates to 0", + ); + } +} + +fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match constant(cx, cx.typeck_results(), e) { + Some((Constant::F64(n), _)) => n.is_nan(), + Some((Constant::F32(n), _)) => n.is_nan(), + _ => false, + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cc5d346b954e3..b72c4c772f1ce 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,8 +1,10 @@ +mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; +mod cast_nan_to_int; mod cast_possible_truncation; mod cast_possible_wrap; mod cast_precision_loss; @@ -569,6 +571,7 @@ declare_clippy_lint! { pedantic, "borrowing just to cast to a raw pointer" } + declare_clippy_lint! { /// ### What it does /// Checks for a raw slice being cast to a slice pointer @@ -596,6 +599,54 @@ declare_clippy_lint! { "casting a slice created from a pointer and length to a slice pointer" } +declare_clippy_lint! { + /// ### What it does + /// 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 + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. + /// + /// ### Example + /// ```rust + /// let string = String::with_capacity(1); + /// let ptr = string.as_ptr() as *mut u8; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```rust + /// let mut string = String::with_capacity(1); + /// let ptr = string.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a known NaN float being cast to an integer + /// + /// ### Why is this bad? + /// NaNs are cast into zero, so one could simply use this and make the + /// code more readable. The lint could also hint at a programmer error. + /// + /// ### Example + /// ```rust,ignore + /// let _: (0.0_f32 / 0.0) as u64; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _: = 0_u64; + /// ``` + #[clippy::version = "1.64.0"] + pub CAST_NAN_TO_INT, + suspicious, + "casting a known floating-point NaN into an integer" +} + pub struct Casts { msrv: Option, } @@ -627,7 +678,9 @@ impl_lint_pass!(Casts => [ CAST_ABS_TO_UNSIGNED, AS_UNDERSCORE, BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS + CAST_SLICE_FROM_RAW_PARTS, + AS_PTR_CAST_MUT, + CAST_NAN_TO_INT, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -653,6 +706,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); @@ -664,6 +718,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc5..c8596987e4d71 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a05b41eb3ab52..0fe973b49a355 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that the type being compared implements `core::cmp::Ord` let ty = cx.typeck_results().expr_ty(lhs1); - let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + let is_ord = cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3ed9cd36a2292..03460689e19ad 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ @@ -55,22 +55,31 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let mut visitor = NumericFallbackVisitor::new(cx); + let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { + matches!(item.kind, ItemKind::Const(..)) + } else { + false + }; + let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } } struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. - ty_bounds: Vec>, + ty_bounds: Vec, cx: &'a LateContext<'tcx>, } impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: vec![if is_parent_const { + ExplicitTyBound(true) + } else { + ExplicitTyBound(false) + }], cx, } } @@ -79,10 +88,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(ty_bound) = self.ty_bounds.last(); + if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !ty_bound.is_numeric(); then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), @@ -123,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -135,7 +143,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -169,7 +177,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { // Visit base with no bound. if let Some(base) = base { - self.ty_bounds.push(TyBound::Nothing); + self.ty_bounds.push(ExplicitTyBound(false)); self.visit_expr(base); self.ty_bounds.pop(); } @@ -192,15 +200,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { - if local.ty.is_some() { - self.ty_bounds.push(TyBound::Any); - } else { - self.ty_bounds.push(TyBound::Nothing); - } - }, + // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` + StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), - _ => self.ty_bounds.push(TyBound::Nothing), + _ => self.ty_bounds.push(ExplicitTyBound(false)), } walk_stmt(self, stmt); @@ -218,28 +221,18 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { - Any, - Ty(Ty<'tcx>), - Nothing, -} +struct ExplicitTyBound(pub bool); -impl<'tcx> TyBound<'tcx> { - fn is_numeric(self) -> bool { - match self { - TyBound::Any => true, - TyBound::Ty(t) => t.is_numeric(), - TyBound::Nothing => false, - } +impl<'tcx> From> for ExplicitTyBound { + fn from(v: Ty<'tcx>) -> Self { + Self(v.is_numeric()) } } -impl<'tcx> From>> for TyBound<'tcx> { +impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - match v { - Some(t) => TyBound::Ty(t), - None => TyBound::Nothing, - } + Self(v.map_or(false, Ty::is_numeric)) } } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 02a16f765b732..a95d9f5390de3 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; @@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, - GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, + self as hir, + def_id::{DefId, LocalDefId}, + BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, @@ -141,7 +145,7 @@ declare_clippy_lint! { "dereferencing when the compiler would automatically dereference" } -impl_lint_pass!(Dereferencing => [ +impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, @@ -149,7 +153,7 @@ impl_lint_pass!(Dereferencing => [ ]); #[derive(Default)] -pub struct Dereferencing { +pub struct Dereferencing<'tcx> { state: Option<(State, StateData)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an @@ -170,11 +174,16 @@ pub struct Dereferencing { /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + // `IntoIterator` for arrays requires Rust 1.53. msrv: Option, } -impl Dereferencing { +impl<'tcx> Dereferencing<'tcx> { #[must_use] pub fn new(msrv: Option) -> Self { Self { @@ -244,7 +253,7 @@ struct RefPat { hir_id: HirId, } -impl<'tcx> LateLintPass<'tcx> for Dereferencing { +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` @@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -682,6 +697,7 @@ impl Position { #[expect(clippy::too_many_lines)] fn walk_parents<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, msrv: Option, ) -> (Position, &'tcx [Adjustment<'tcx>]) { @@ -796,7 +812,16 @@ fn walk_parents<'tcx>( Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), None => { if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) .position_for_arg() @@ -843,7 +868,16 @@ fn walk_parents<'tcx>( args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; if let ty::Param(param_ty) = ty.kind() { - needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i + 1, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability( cx, @@ -1017,8 +1051,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. +#[expect(clippy::too_many_arguments)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, parent: &Expr<'tcx>, arg_index: usize, param_ty: ParamTy, @@ -1081,10 +1117,13 @@ fn needless_borrow_impl_arg_position<'tcx>( // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); - let mut check_referent = |referent| { + let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) { + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { return false; } @@ -1125,7 +1164,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let mut needless_borrow = false; while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_referent(referent) { + if !check_reference_and_referent(expr, referent) { break; } expr = referent; @@ -1153,6 +1192,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + let mir = enclosing_mir(cx.tcx, reference.hir_id); + if let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.has_deref() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -1437,8 +1506,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } } -impl Dereferencing { - fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { +impl<'tcx> Dereferencing<'tcx> { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 3fac93dcc90c5..fad984d05ca95 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -339,10 +339,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, }; - let copy_id = match cx.tcx.lang_items().copy_trait() { - Some(id) => id, - None => return, - }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return }; let (ty_adt, ty_subs) = match *ty.kind() { // Unions can't derive clone. ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1a381f92c0314..6ac85606d9c7c 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -94,9 +94,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { } else { path_def_id(cx, expr) }; - let def_id = match uncalled_path.or_else(|| fn_def_id(cx, expr)) { - Some(def_id) => def_id, - None => 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], diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 9c834cf014485..b44e62435881f 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -65,28 +65,24 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { - Some(higher::If { cond, then, r#else }) => (cond, then, r#else), - _ => return, + let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else { + return }; - let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { - Some(x) => x, - None => return, + let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else { + return }; - let then_search = match find_insert_calls(cx, &contains_expr, then_expr) { - Some(x) => x, - None => return, + let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else { + return }; let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; let sugg = if let Some(else_expr) = else_expr { - let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) => search, - None => return, + let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { + return; }; if then_search.edits.is_empty() && else_search.edits.is_empty() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 3732410e71e57..7b9786d7e570f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -213,9 +213,8 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc if !closure_ty.has_late_bound_regions() { return true; } - let substs = match closure_ty.kind() { - ty::Closure(_, substs) => substs, - _ => return false, + let ty::Closure(_, substs) = closure_ty.kind() else { + return false; }; let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal); cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 99bef62f81436..32073536b45bb 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; +use clippy_utils::macros::{ + is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, +}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; @@ -13,6 +15,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::DefId; +use rustc_span::edition::Edition::Edition2021; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -111,11 +115,47 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } -impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detects [formatting parameters] that have no effect on the output of + /// `format!()`, `println!()` or similar macros. + /// + /// ### Why is this bad? + /// Shorter format specifiers are easier to read, it may also indicate that + /// an expected formatting operation such as adding padding isn't happening. + /// + /// ### Example + /// ```rust + /// println!("{:.}", 1.0); + /// + /// println!("not padded: {:5}", format_args!("...")); + /// ``` + /// Use instead: + /// ```rust + /// println!("{}", 1.0); + /// + /// println!("not padded: {}", format_args!("...")); + /// // OR + /// println!("padded: {:5}", format!("...")); + /// ``` + /// + /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters + #[clippy::version = "1.66.0"] + pub UNUSED_FORMAT_SPECS, + complexity, + "use of a format specifier that has no effect" +} + +impl_lint_pass!(FormatArgs => [ + FORMAT_IN_FORMAT_ARGS, + TO_STRING_IN_FORMAT_ARGS, + UNINLINED_FORMAT_ARGS, + UNUSED_FORMAT_SPECS, +]); pub struct FormatArgs { msrv: Option, @@ -130,27 +170,26 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr); - let expr_expn_data = expr.span.ctxt().outer_expn_data(); - let outermost_expn_data = outermost_expn_data(expr_expn_data); - if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if is_format_macro(cx, macro_def_id); - if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - then { - for arg in &format_args.args { - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr) + && let expr_expn_data = expr.span.ctxt().outer_expn_data() + && let outermost_expn_data = outermost_expn_data(expr_expn_data) + && let Some(macro_def_id) = outermost_expn_data.macro_def_id + && is_format_macro(cx, macro_def_id) + && let ExpnKind::Macro(_, name) = outermost_expn_data.kind + { + for arg in &format_args.args { + check_unused_format_specifier(cx, arg); + if !arg.format.is_default() { + continue; } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + if is_aliased(&format_args, arg.param.value.hir_id) { + continue; } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); + } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); } } } @@ -158,10 +197,84 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { extract_msrv_attr!(LateContext); } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { + let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); + + if let Count::Implied(Some(mut span)) = arg.format.precision + && !span.is_empty() + { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + span, + "empty precision specifier has no effect", + |diag| { + if param_ty.is_floating_point() { + diag.note("a precision specifier is not required to format floats"); + } + + if arg.format.is_default() { + // If there's no other specifiers remove the `:` too + span = arg.format_span(); + } + + diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); + }, + ); + } + + if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + arg.span, + "format specifiers have no effect on `format_args!()`", + |diag| { + let mut suggest_format = |spec, span| { + let message = format!("for the {spec} to apply consider using `format!()`"); + + if let Some(mac_call) = root_macro_call(arg.param.value.span) + && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) + && arg.span.eq_ctxt(mac_call.span) + { + diag.span_suggestion( + cx.sess().source_map().span_until_char(mac_call.span, '!'), + message, + "format", + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = span { + diag.span_help(span, message); + } + }; + + if !arg.format.width.is_implied() { + suggest_format("width", arg.format.width.span()); + } + + if !arg.format.precision.is_implied() { + suggest_format("precision", arg.format.precision.span()); + } + + diag.span_suggestion_verbose( + arg.format_span(), + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { if args.format_string.span.from_expansion() { return; } + if call_site.edition() < Edition2021 && is_panic(cx, def_id) { + // panic! before 2021 edition considers a single string argument as non-format + return; + } let mut fixes = Vec::new(); // If any of the arguments are referenced by an index number, @@ -248,7 +361,7 @@ fn check_format_in_format_args( fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); - if let ExprKind::MethodCall(_, receiver, [], _) = value.kind; + if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); if is_diag_trait_item(cx, method_def_id, sym::ToString); let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -264,7 +377,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex span_lint_and_sugg( cx, TO_STRING_IN_FORMAT_ARGS, - value.span.with_lo(receiver.span.hi()), + to_string_span.with_lo(receiver.span.hi()), &format!( "`to_string` applied to a type that implements `Display` in `{name}!` args" ), diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5d25c1d06341f..95eda4ea88275 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::span_is_local; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs, path_def_id}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::{ + GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, + TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -54,28 +62,152 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } - if_chain! { - if let hir::ItemKind::Impl{ .. } = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id); - - then { - span_lint_and_help( - cx, - FROM_OVER_INTO, - cx.tcx.sess.source_map().guess_head_span(item.span), - "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", - None, - &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(hir_trait_ref), + self_ty, + items: [impl_item_ref], + .. + }) = item.kind + && 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 + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.def_id) + && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + { + span_lint_and_then( + cx, + FROM_OVER_INTO, + cx.tcx.sess.source_map().guess_head_span(item.span), + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", + |diag| { + // If the target type is likely foreign mention the orphan rules as it's a common source of confusion + if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) { + diag.help( + "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" + ); + } + + let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty()); + if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + } else { + diag.help(message); + } + }, + ); } } extract_msrv_attr!(LateContext); } + +/// Finds the occurences of `Self` and `self` +struct SelfFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Occurences of `Self` + upper: Vec, + /// Occurences of `self` + lower: Vec, + /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding + /// already named `val` + invalid: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { + for segment in path.segments { + match segment.ident.name { + kw::SelfLower => self.lower.push(segment.ident.span), + kw::SelfUpper => self.upper.push(segment.ident.span), + _ => continue, + } + } + + self.invalid |= path.span.from_expansion(); + if !self.invalid { + walk_path(self, path); + } + } + + fn visit_name(&mut self, name: Symbol) { + if name == sym::val { + self.invalid = true; + } + } +} + +fn convert_to_from( + cx: &LateContext<'_>, + into_trait_seg: &PathSegment<'_>, + target_ty: &Ty<'_>, + self_ty: &Ty<'_>, + impl_item_ref: &ImplItemRef, +) -> Option> { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let body = cx.tcx.hir().body(body_id); + let [input] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + + let from = snippet_opt(cx, self_ty.span)?; + let into = snippet_opt(cx, target_ty.span)?; + + let mut suggestions = vec![ + // impl Into for U -> impl From for U + // ~~~~ ~~~~ + (into_trait_seg.ident.span, String::from("From")), + // impl Into for U -> impl Into for U + // ~ ~ + (target_ty.span, from.clone()), + // impl Into for U -> impl Into for T + // ~ ~ + (self_ty.span, into), + // fn into(self) -> T -> fn from(self) -> T + // ~~~~ ~~~~ + (impl_item.ident.span, String::from("from")), + // fn into([mut] self) -> T -> fn into([mut] v: T) -> T + // ~~~~ ~~~~ + (self_ident.span, format!("val: {from}")), + // fn into(self) -> T -> fn into(self) -> Self + // ~ ~~~~ + (sig.decl.output.span(), String::from("Self")), + ]; + + let mut finder = SelfFinder { + cx, + upper: Vec::new(), + lower: Vec::new(), + invalid: false, + }; + finder.visit_expr(body.value); + + if finder.invalid { + return None; + } + + // don't try to replace e.g. `Self::default()` with `&[T]::default()` + if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) { + return None; + } + + for span in finder.upper { + suggestions.push((span, from.clone())); + } + for span in finder.lower { + suggestions.push((span, String::from("val"))); + } + + Some(suggestions) +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d263804f32cf4..3064b6c9d22f8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -7,14 +7,14 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -181,7 +181,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) } } -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { match *ty.kind() { @@ -189,7 +189,9 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, substs) => { tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path)) + || KNOWN_WRAPPER_TYS + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index f83f8b40f94b7..bd473ac7e51b0 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -22,9 +22,8 @@ pub(super) fn check_fn( return; } - let code_snippet = match snippet_opt(cx, body.value.span) { - Some(s) => s, - _ => return, + let Some(code_snippet) = snippet_opt(cx, body.value.span) else { + return }; let mut line_count: u64 = 0; let mut in_comment = false; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 48edbf6ae576c..29d59c26d92c4 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.44.0"] pub IMPLICIT_SATURATING_SUB, - pedantic, + style, "Perform saturating subtraction instead of implicitly checking lower bound of data type" } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 36e03e50a8e4f..0ef77e03de906 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -145,9 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { - val - } else { + let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { return; }; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index eb13d0869c037..8ed7e4bb196cd 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -124,9 +124,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let (adt, subst) = match ty.kind() { - Adt(adt, subst) => (adt, subst), - _ => panic!("already checked whether this is an enum"), + let Adt(adt, subst) = ty.kind() else { + panic!("already checked whether this is an enum") }; if adt.variants().len() <= 1 { return; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 176787497ebf2..b7798b1c1d749 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -99,10 +100,9 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); -const SYNC_GUARD_PATHS: [&[&str]; 6] = [ - &paths::MUTEX_GUARD, - &paths::RWLOCK_READ_GUARD, - &paths::RWLOCK_WRITE_GUARD, +const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; + +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_RWLOCK_READ_GUARD, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, @@ -121,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) + SYNC_GUARD_SYMS + .iter() + .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) + || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) }, GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, @@ -134,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on a synchronization lock", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if init_ty.needs_drop(cx.tcx, cx.param_env) { span_lint_and_help( @@ -144,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( @@ -153,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on an expression with `#[must_use]` type", None, - "consider explicitly using expression value" + "consider explicitly using expression value", ); } else if is_must_use_func_call(cx, init) { span_lint_and_help( @@ -162,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on a result of a `#[must_use]` function", None, - "consider explicitly using function result" + "consider explicitly using function result", ); } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index fe1f0b56646cd..f5ad52ba1892a 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), @@ -71,6 +72,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format::USELESS_FORMAT), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), @@ -87,6 +90,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -136,6 +140,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a58d066fa6b67..8be9dc4baf193 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_parens::DOUBLE_PARENS), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -27,6 +28,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index 71dfdab369b97..40c94c6e8d33d 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -3,20 +3,20 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), + LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), + LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), + LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 306cb6a61c943..800e3a8767133 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -4,37 +4,37 @@ store.register_lints(&[ #[cfg(feature = "internal")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_DEPRECATION_REASON, + utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal")] - utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, #[cfg(feature = "internal")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::invalid_paths::INVALID_PATHS, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_PATHS, + utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, #[cfg(feature = "internal")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_MSRV_ATTR_IMPL, + utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, #[cfg(feature = "internal")] - utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_DEF_PATH, + utils::internal_lints::produce_ice::PRODUCE_ICE, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, + utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, @@ -66,12 +66,14 @@ store.register_lints(&[ cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, + casts::AS_PTR_CAST_MUT, casts::AS_UNDERSCORE, casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, + casts::CAST_NAN_TO_INT, casts::CAST_POSSIBLE_TRUNCATION, casts::CAST_POSSIBLE_WRAP, casts::CAST_PRECISION_LOSS, @@ -162,6 +164,7 @@ store.register_lints(&[ format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, format_args::UNINLINED_FORMAT_ARGS, + format_args::UNUSED_FORMAT_SPECS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -258,6 +261,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_FILTER, matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, @@ -403,6 +407,7 @@ store.register_lints(&[ missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + missing_trait_methods::MISSING_TRAIT_METHODS, mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, @@ -475,6 +480,7 @@ store.register_lints(&[ panic_unimplemented::TODO, panic_unimplemented::UNIMPLEMENTED, panic_unimplemented::UNREACHABLE, + partial_pub_fields::PARTIAL_PUB_FIELDS, partialeq_ne_impl::PARTIALEQ_NE_IMPL, partialeq_to_none::PARTIALEQ_TO_NONE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e0b4639af53e6..65616d28d8f10 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(casts::AS_PTR_CAST_MUT), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index bc2f0beb358a5..060d3bdc7c6fa 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -29,12 +29,10 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6eb9b3d3b9b7a..f62d57af5b47f 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS), LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), @@ -61,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::TODO), LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 8e1390167dc81..6894d69e928a7 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -25,12 +25,14 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index d6d95c95c85d2..b70c4bb73e57d 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 89ffca8128a9e..1307096b28d7b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -39,7 +39,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; @@ -291,6 +290,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; @@ -326,6 +326,7 @@ mod option_if_let_else; mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; @@ -419,7 +420,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -435,7 +436,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { .and_then(|v| parse_msrv(&v, None, None)); let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -446,7 +447,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { if let Some(clippy_msrv) = clippy_msrv { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { - sess.warn(&format!( + sess.warn(format!( "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -475,7 +476,7 @@ pub fn read_conf(sess: &Session) -> Conf { let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(error) @@ -483,7 +484,7 @@ pub fn read_conf(sess: &Session) -> Conf { } for warning in warnings { - sess.struct_warn(&format!( + sess.struct_warn(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(warning) @@ -530,17 +531,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal")] { - store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); - store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); - store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); + store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal)); + 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::if_chain_style::IfChainStyle)); + store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); @@ -910,6 +917,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); + store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); + store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 00cfc6d49f19a..27ba27202bf7e 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx + let extent = self + .cx .tcx .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) @@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } else { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - return false; // no need to walk further *on the variable* - } + return false; // no need to walk further *on the variable* + }, _ => (), } } @@ -302,17 +304,26 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if let Some(trait_id) = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); + if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); - then { return } + then { + return; + } } if_chain! { // an index op if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); - then { return } + then { + return; + } } if_chain! { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 153f97e4e66c8..55989f8a44650 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -331,9 +331,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & } if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) { - let local_id = match iter_expr.path { - Res::Local(id) => id, - _ => return true, + let Res::Local(local_id) = iter_expr.path else { + return true }; let mut v = NestedLoopVisitor { cx, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 9a0a26c0991b3..090f9f8ff73cf 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call; -use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -140,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if args.bindings.len() == 1; let binding = &args.bindings[0]; if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind; + if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; then { - return Some(output) + return Some(output); } } @@ -175,9 +174,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if let Some(args) = cx + .tcx + .lang_items() + .from_generator_fn() + .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; - if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; + if let Expr { + kind: ExprKind::Closure(&Closure { body, .. }), + .. + } = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index ece4df95505ce..02dc8755dd61c 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -12,9 +12,9 @@ use std::ops::Deref; use clippy_utils::{ diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, get_trait_def_id, + eq_expr_value, higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sugg::Sugg, ty::implements_trait, @@ -190,7 +190,11 @@ impl TypeClampability { fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { if ty.is_floating_point() { Some(TypeClampability::Float) - } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + } else if cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { Some(TypeClampability::Ord) } else { None diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df348..33a052c41a38a 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs new file mode 100644 index 0000000000000..66ba1f6f9c550 --- /dev/null +++ b/clippy_lints/src/matches/manual_filter.rs @@ -0,0 +1,153 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::contains_unsafe_block; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_FILTER; + +// Function called on the of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the form `=if {} else {}` +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` +// return the `cond` expression if so. +fn get_cond_expr<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> Option> { + if_chain! { + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); + if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; + if let PatKind::Binding(_,target, ..) = pat.kind; + if let (then_visitor, else_visitor) + = (is_some_expr(cx, target, ctxt, then_expr), + is_some_expr(cx, target, ctxt, else_expr)); + if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + then { + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: contains_unsafe_block(cx, expr), + needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + }) + } + }; + None +} + +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's + // checked by `contains_unsafe_block` + if let ExprKind::Block(block, None) = expr.kind { + if block.stmts.is_empty() { + return block.expr; + } + }; + None +} + +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { + peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) +} + +// function called for each expression: +// Some(x) => if { +// +// } else { +// +// } +// Returns true if resolves to `Some(x)`, `false` otherwise +fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + // there can be not statements in the block as they would be removed when switching to `.filter` + if let ExprKind::Call(callee, [arg]) = inner_expr.kind { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); + } + }; + false +} + +// given the closure: `|| ` +// returns `|&| ` +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { + if has_copy_trait { + let mut with_ampersand = body_str; + with_ampersand.insert(1, '&'); + with_ampersand + } else { + body_str + } +} + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, ty, sym::Option) + && let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_cond_expr, + ) { + let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy); + span_lint_and_sugg( + cx, + MANUAL_FILTER, + expr.span, + "manual implementation of `Option::filter`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.filter({body_str}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str + ) + } else { + format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str) + }, + sugg_info.app, + ); + } +} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 76f5e1c941c7a..aaba239677fff 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,22 +1,13 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; -use super::MANUAL_MAP; +use clippy_utils::{is_res_lang_ctor, path_res}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::SyntaxContext; pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, @@ -43,7 +34,6 @@ pub(super) fn check_if_let<'tcx>( check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); } -#[expect(clippy::too_many_lines)] fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -53,254 +43,74 @@ fn check<'tcx>( else_pat: Option<&'tcx Pat<'_>>, else_body: &'tcx Expr<'_>, ) { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_some_expr, ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } - - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({scrutinee_str})") - } else { - scrutinee_str.into() - }; - - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.map({}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {expr_snip}") - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {expr_snip} }}") - } else { - format!("|{pat_snip}| {expr_snip}") - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") - } else { - format!("{scrutinee_str}{as_ref_str}.map({body_str})") - }, - app, - ); -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) + format!( + "{}{}.map({})", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) }, - _ => None, - } + sugg_info.app, + ); } - f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, + _: &'tcx Pat<'_>, expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, ctxt: SyntaxContext, ) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => - { - Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }) - }, - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. + fn get_some_expr_internal<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, + ) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr_internal( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + get_some_expr_internal(cx, expr, false, ctxt) } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs new file mode 100644 index 0000000000000..5b7644a538323 --- /dev/null +++ b/clippy_lints/src/matches/manual_utils.rs @@ -0,0 +1,277 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return None; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return None, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return None; + } + + let Some(some_expr) = get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) else { + return None; + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return None; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return None; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return None, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({scrutinee_str})") + } else { + scrutinee_str.into() + }; + + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; + + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{pat_snip}| {closure_expr_snip}") + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return None; + }; + + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, + app, + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +#[derive(Debug)] +pub(super) enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 37049f8357751..168c1e4d2e60d 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -221,7 +221,6 @@ fn iter_matching_struct_fields<'a>( #[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, @@ -235,9 +234,8 @@ impl<'a> NormalizedPat<'a> { Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields) }, PatKind::TupleStruct(ref path, pats, wild_idx) => { - let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() { - Some(x) => x, - None => return Self::Wild, + let Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else { + return Self::Wild }; let (var_id, variant) = if adt.is_enum() { match cx.qpath_res(path, pat.hir_id).opt_def_id() { diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 68682cedf1de4..1bf8d4e96ad49 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,6 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), + true, ); span_lint_and_sugg( @@ -90,6 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, + true, ); (expr.span, sugg) }, @@ -107,10 +109,14 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }, PatKind::Wild => { if ex.can_have_side_effects() { - let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); - let sugg = format!( - "{};\n{indent}{snippet_body}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability) + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &snippet_body, + &mut applicability, + None, + false, ); span_lint_and_sugg( @@ -169,6 +175,7 @@ fn sugg_with_curlies<'a>( snippet_body: &str, applicability: &mut Applicability, assignment: Option, + needs_var_binding: bool, ) -> String { let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); @@ -200,9 +207,15 @@ fn sugg_with_curlies<'a>( s }); - format!( - "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) - ) + let scrutinee = if needs_var_binding { + format!( + "let {} = {}", + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability) + ) + } else { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + }; + + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e6b183fc05f25..7d8171ead89e1 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,7 +1,9 @@ mod collapsible_match; mod infallible_destructuring_match; +mod manual_filter; mod manual_map; mod manual_unwrap_or; +mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -898,6 +900,34 @@ declare_clippy_lint! { "reimplementation of `map`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `filter` + /// + /// ### Why is this bad? + /// Using the `filter` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => if x % 2 == 0 { + /// Some(x) + /// } else { + /// None + /// }, + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).filter(|&x| x % 2 == 0); + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_FILTER, + complexity, + "reimplentation of `filter`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -939,6 +969,7 @@ impl_lint_pass!(Matches => [ SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, MANUAL_MAP, + MANUAL_FILTER, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -988,6 +1019,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); + manual_filter::check_match(cx, ex, arms, expr); } if self.infallible_destructuring_match_linted { @@ -1014,6 +1046,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !in_constant(cx, expr.hir_id) { manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + manual_filter::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d496107ffd6b8..e5a15b2e1a1d2 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -156,10 +155,10 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> /// Returns `true` if the given type is an enum we know won't be expanded in the future fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; + let candidates = [sym::Cow, sym::Option, sym::Result]; for candidate_ty in candidates { - if match_type(cx, ty, candidate_ty) { + if is_type_diagnostic_item(cx, ty, candidate_ty) { return true; } } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index be56b63506a4b..8adf9e3705920 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -42,9 +42,8 @@ pub(super) fn check( fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind() { - ty::Ref(_, _, mutbl) => mutbl, - _ => unreachable!(), + let ty::Ref(_, _, mutbl) = self_ref_ty.kind() else { + unreachable!() }; let method_name = match mutbl { hir::Mutability::Not => "iter", diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ec694cf6882e5..b80541b86479a 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -21,11 +21,7 @@ pub fn check( return; } - let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) { - mm - } else { - return; - }; + let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return }; if ty.is_signed() { use self::{ @@ -33,9 +29,7 @@ pub fn check( Sign::{Neg, Pos}, }; - let sign = if let Some(sign) = lit_sign(arith_rhs) { - sign - } else { + let Some(sign) = lit_sign(arith_rhs) else { return; }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cfcf9596c50d3..fb92779be2a7a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -102,9 +102,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, -}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -3372,7 +3370,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3380,7 +3380,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { first_arg_ty, first_arg_span, false, - true + true, ); } } @@ -3389,7 +3389,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); if !ret_ty.contains(self_ty); then { @@ -3846,14 +3848,13 @@ impl SelfKind { return m == mutability && t == parent_ty; } - let trait_path = match mutability { - hir::Mutability::Not => &paths::ASREF_TRAIT, - hir::Mutability::Mut => &paths::ASMUT_TRAIT, + let trait_sym = match mutability { + hir::Mutability::Not => sym::AsRef, + hir::Mutability::Mut => sym::AsMut, }; - let trait_def_id = match get_trait_def_id(cx, trait_path) { - Some(did) => did, - None => return false, + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6fb92d1c663cf..742483e6b2e55 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -32,8 +32,7 @@ pub(super) fn check<'tcx>( return; } - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, + let deref_aliases: [&[&str]; 8] = [ &paths::DEREF_MUT_TRAIT_METHOD, &paths::CSTRING_AS_C_STR, &paths::OS_STRING_AS_OS_STR, @@ -45,12 +44,14 @@ pub(super) fn check<'tcx>( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .map_or(false, |fun_def_id| { - deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) - }), + hir::ExprKind::Path(ref expr_qpath) => { + cx.qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) + || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }) + }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); @@ -68,7 +69,8 @@ pub(super) fn check<'tcx>( if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6a35024d03612..991d3dd538bf8 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Symbol}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -88,11 +88,11 @@ pub(super) fn check<'tcx>( fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ + (sym::BTreeEntry, false, &["or_insert"], "with"), + (sym::HashMapEntry, false, &["or_insert"], "with"), + (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (sym::Result, true, &["or", "unwrap_or"], "else"), ]; if_chain! { @@ -104,7 +104,7 @@ pub(super) fn check<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); if poss.contains(&name); @@ -121,10 +121,9 @@ pub(super) fn check<'tcx>( macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped + None => macro_expanded_snipped, } - } - else { + } else { not_macro_argument_snippet } }; diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index ae3594bd36c3a..1acac59144cee 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -289,9 +289,7 @@ fn parse_iter_usage<'tcx>( ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind { - (name, args) - } else { + let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else { return None; }; let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 5cf88bfc8880d..3566fe9a0bbc9 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -363,7 +363,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< && let output_ty = return_ty(cx, item.hir_id()) && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id()) && Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id()); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id()); fn_ctxt.can_coerce(ty, output_ty) }) { if has_lifetime(output_ty) && has_lifetime(ty) { diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 62c6ca32d31a9..27e7f8505eb5b 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -6,9 +6,7 @@ use rustc_lint::EarlyContext; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; // Do not lint when literal is unsuffixed. diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 80e2421310078..263ee1e945a25 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -5,9 +5,7 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; if maybe_last_sep_idx <= 2 { diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4963bba82f2da..9ead43ea4a477 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,6 +6,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { + let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -15,15 +16,18 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { diag.span_suggestion( lit.span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, ); + // do not advise to use octal form if the literal cannot be expressed in base 8. + if !lit_snip.contains(|c| c == '8' || c == '9') { + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{trimmed_lit_snip}"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 6dd76a6531e49..9de4b56b77b56 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -70,9 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // find the type that the Impl is for // only lint on struct/enum/union for now - let defid = match path.res { - Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, - _ => return, + let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { + return }; // get the names of the generic parameters in the type diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 0000000000000..68af8a672f6ae --- /dev/null +++ b/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + for assoc in provided.values() { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + } + } +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index a2419c277e9c2..6752976348f60 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -190,10 +190,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { if parent_id == cur_id { break; } - let parent_node = match map.find(parent_id) { - Some(parent) => parent, - None => break, - }; + let Some(parent_node) = map.find(parent_id) else { break }; let stop_early = match parent_node { Node::Expr(expr) => check_expr(vis, expr), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3233d87c07319..c3b633fd6a038 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -49,9 +49,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 expr = match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, - _ => return, + let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { + return }; if_chain! { 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 a7e0e35787cff..5c2b96f5b2ce6 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -47,18 +47,16 @@ 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_chain! { - if !in_external_macro(cx.sess(), expr.span); if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { - let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { implements_trait(cx, ty, id, &[]) } else { return; @@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable" + clear that the two values could be incomparable", ); } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 2c839d029c6f7..a6742824bc56a 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -357,9 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - let item_def_id = match cx.qpath_res(qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, - _ => return, + let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { + return }; // Climb up to resolve any field access and explicit referencing. diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 1a765b14892f6..2ecb04874842f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let ExprKind::Lit(_) = param.kind; then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, + let Some(snip) = snippet_opt(cx, param.span) else { + return }; if !snip.starts_with("0o") { @@ -72,16 +71,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); if let ExprKind::Lit(_) = param.kind; - + if let Some(snip) = snippet_opt(cx, param.span); + if !snip.starts_with("0o"); then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + show_error(cx, param); } } }, diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 0ca0befc13515..6c909e5ed73ea 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, + .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index c9c777f1bd8d8..24aeb82a37f31 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs new file mode 100644 index 0000000000000..f60d9d65b1207 --- /dev/null +++ b/clippy_lints/src/partial_pub_fields.rs @@ -0,0 +1,81 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether partial fields of a struct are public. + /// + /// Either make all fields of a type public, or make none of them public + /// + /// ### Why is this bad? + /// Most types should either be: + /// * Abstract data types: complex objects with opaque implementation which guard + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together. + /// + /// ### Example + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// b: u8, + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// pub b: u8, + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub PARTIAL_PUB_FIELDS, + restriction, + "partial fields of a struct are public" +} +declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); + +impl EarlyLintPass for PartialPubFields { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let ItemKind::Struct(ref st, _) = item.kind else { + return; + }; + + let mut fields = st.fields().iter(); + let Some(first_field) = fields.next() else { + // Empty struct. + return; + }; + let all_pub = first_field.vis.kind.is_pub(); + let all_priv = !all_pub; + + let msg = "mixed usage of pub and non-pub fields"; + + for field in fields { + if all_priv && field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using private field here", + ); + return; + } else if all_pub && !field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using public field here", + ); + return; + } + } + } +} diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index d296a150b46d0..40db315bf2726 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,13 +15,17 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::fmt; use std::iter; @@ -384,6 +388,17 @@ enum DerefTy<'tcx> { Slice(Option, Ty<'tcx>), } impl<'tcx> DerefTy<'tcx> { + fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { + match *self { + Self::Str => cx.tcx.types.str_, + Self::Path => cx.tcx.mk_adt( + cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), + List::empty(), + ), + Self::Slice(_, ty) => cx.tcx.mk_slice(ty), + } + } + fn argless_str(&self) -> &'static str { match *self { Self::Str => "str", @@ -552,9 +567,8 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } // Check if this is local we care about - let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) { - Some(&i) => i, - None => return walk_expr(self, e), + let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + return walk_expr(self, e); }; let args = &self.args[args_idx]; let result = &mut self.results[args_idx]; @@ -582,6 +596,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { match *ty.skip_binder().peel_refs().kind() { + ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), ty::Param(_) => true, ty::Adt(def, _) => def.did() == args.ty_did, _ => false, @@ -609,14 +624,15 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } } - let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) { - x - } else { + let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else { set_skip_flag(); return; }; match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { + set_skip_flag(); + }, ty::Param(_) => { set_skip_flag(); }, @@ -668,6 +684,30 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: v.results } +fn matches_preds<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>], +) -> bool { + let infcx = cx.tcx.infer_ctxt().build(); + preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + ExistentialPredicate::Trait(p) => infcx + .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) + .must_apply_modulo_regions(), + ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + cx.param_env, + cx.tcx.mk_predicate(Binder::bind_with_vars( + PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), + List::empty(), + )), + )), + ExistentialPredicate::AutoTrait(p) => infcx + .type_implements_trait(p, ty, List::empty(), cx.param_env) + .must_apply_modulo_regions(), + }) +} + fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index b0a5d1a675828..72dda67c72b25 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -49,15 +49,13 @@ declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) { - Some(call_arg) => call_arg, - None => return, + let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { + return }; // Check if the argument to the method call is a cast from usize - let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) { - Some(cast_lhs_expr) => cast_lhs_expr, - None => return, + let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { + return }; let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9fd86331ec755..aedbe08e3e46e 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,25 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, HirId}; -use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{ - self, traversal, - visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, - Mutability, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, Ty}; -use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::ops::ControlFlow; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -89,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let possible_origin = { - let mut vis = PossibleOriginVisitor::new(mir); - vis.visit_body(mir); - vis.into_map(cx) - }; - let maybe_storage_live_result = MaybeStorageLive - .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") - .iterate_to_fixpoint() - .into_results_cursor(mir); - let mut possible_borrower = { - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - }; + let mut possible_borrower = PossibleBorrowerMap::new(cx, mir); for (bb, bbdata) in mir.basic_blocks.iter_enumerated() { let terminator = bbdata.terminator(); @@ -374,403 +353,40 @@ struct CloneUsage { /// Whether the clone value is mutated. clone_consumed_or_mutated: bool, } -fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { - struct V { - cloned: mir::Local, - clone: mir::Local, - result: CloneUsage, - } - impl<'tcx> mir::visit::Visitor<'tcx> for V { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); - } - - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { - let local = place.local; - - if local == self.cloned - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.result.cloned_used = true; - self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { - matches!( - ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) - ) - .then(|| loc) - }); - } else if local == self.clone { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.result.clone_consumed_or_mutated = true; - }, - _ => {}, - } - } - } - } - - let init = CloneUsage { - cloned_used: false, - cloned_consume_or_mutate_loc: None, - // Consider non-temporary clones consumed. - // TODO: Actually check for mutation of non-temporaries. - clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, - }; - traversal::ReversePostorder::new(mir, bb) - .skip(1) - .fold(init, |usage, (tbb, tdata)| { - // Short-circuit - if (usage.cloned_used && usage.clone_consumed_or_mutated) || - // Give up on loops - tdata.terminator().successors().any(|s| s == bb) - { - return CloneUsage { - cloned_used: true, - clone_consumed_or_mutated: true, - ..usage - }; - } - - let mut v = V { - cloned, - clone, - result: usage, - }; - v.visit_basic_block_data(tbb, tdata); - v.result - }) -} - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} - -/// Collects the possible borrowers of each local. -/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` -/// possible borrowers of `a`. -struct PossibleBorrowerVisitor<'a, 'tcx> { - possible_borrower: TransitiveRelation, - body: &'a mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, -} - -impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'a mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, - } - } - - fn into_map( - self, - cx: &LateContext<'tcx>, - maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'a, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); - } - }); - }, - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { - if let mir::TerminatorKind::Call { - args, - destination: mir::Place { local: dest, .. }, - .. - } = &terminator.kind - { - // TODO add doc - // If the call returns something with lifetimes, - // let's conservatively assume the returned value contains lifetime of all the arguments. - // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - - let mut immutable_borrowers = vec![]; - let mut mutable_borrowers = vec![]; - - for op in args { - match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => { - if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { - mutable_borrowers.push(p.local); - } else { - immutable_borrowers.push(p.local); - } - }, - mir::Operand::Constant(..) => (), - } - } - - let mut mutable_variables: Vec = mutable_borrowers - .iter() - .filter_map(|r| self.possible_origin.get(r)) - .flat_map(HybridBitSet::iter) - .collect(); - - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { - mutable_variables.push(*dest); - } - - for y in mutable_variables { - for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); - } - for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); - } - } - } - } -} - -/// Collect possible borrowed for every `&mut` local. -/// For example, `_1 = &mut _2` generate _1: {_2,...} -/// Known Problems: not sure all borrowed are tracked -struct PossibleOriginVisitor<'a, 'tcx> { - possible_origin: TransitiveRelation, - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - Self { - possible_origin: TransitiveRelation::default(), - body, - } - } - - fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - map - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | - // _2: &mut _; - // _3 = move _2 - mir::Rvalue::Use(mir::Operand::Move(borrowed)) | - // _3 = move _2 as &mut _; - mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) - => { - self.possible_origin.add(lhs, borrowed.local); - }, - _ => {}, - } - } -} - -struct ContainsRegion; - -impl TypeVisitor<'_> for ContainsRegion { - type BreakTy = (); - - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { - ControlFlow::BREAK - } -} - -fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; - - let mut visit_op = |op: &mir::Operand<'_>| match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - mir::Operand::Constant(..) => (), - }; - match rvalue { - Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), - Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { - visit_op(lhs); - visit_op(rhs); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + if let Some(( + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, }, - _ => (), - } -} - -/// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrowerMap<'a, 'tcx> { - /// Mapping `Local -> its possible borrowers` - map: FxHashMap>, - maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - bitset: (BitSet, BitSet), -} - -impl PossibleBorrowerMap<'_, '_> { - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); - } - } else { - return false; - } - - self.bitset.1.clear(); - for b in borrowers { - self.bitset.1.insert(*b); + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) + .map(|mut vec| (vec.remove(0), vec.remove(0))) + { + CloneUsage { + cloned_used: !cloned_use_locs.is_empty(), + cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp + || !clone_consume_or_mutate_locs.is_empty(), } - - self.bitset.0 == self.bitset.1 - } - - fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - self.maybe_live.contains(local) - } -} - -#[derive(Default)] -struct TransitiveRelation { - relations: FxHashMap>, -} -impl TransitiveRelation { - fn add(&mut self, a: mir::Local, b: mir::Local) { - self.relations.entry(a).or_default().push(b); - } - - fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { - let mut seen = HybridBitSet::new_empty(domain_size); - let mut stack = vec![a]; - while let Some(u) = stack.pop() { - if let Some(edges) = self.relations.get(&u) { - for &v in edges { - if seen.insert(v) { - stack.push(v); - } - } - } + } else { + CloneUsage { + cloned_used: true, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, } - seen } } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1c..f21b3ea6c3b05 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 5dcdab5b8ab90..87f966ced0df1 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -106,10 +106,7 @@ impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - let (id, ident) = match pat.kind { - PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident), - _ => return, - }; + let PatKind::Binding(_, id, ident, _) = pat.kind else { return }; if pat.span.desugaring_kind().is_some() { return; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 70d166c4854c4..2f190e594a831 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -6,11 +6,7 @@ use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment -pub(super) fn is_layout_incompatible<'tcx>( - cx: &LateContext<'tcx>, - from: Ty<'tcx>, - to: Ty<'tcx>, -) -> bool { +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) @@ -33,9 +29,7 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::{ - AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast, - }; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) @@ -46,20 +40,18 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> Option { +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id); // If we already have errors, we can't be sure we can pointer cast. - assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors"); + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); if let Ok(check) = cast::CastCheck::new( &fn_ctxt, e, from_ty, to_ty, diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 6b9de64e24c93..fa567b9b2d243 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -9,6 +9,7 @@ use rustc_span::symbol::sym; use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let app = Applicability::Unspecified; if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { span_lint_and_sugg( @@ -18,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Rc` when T is a buffer type", "try", format!("Rc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else { let Some(ty) = qpath_generic_tys(qpath).next() else { return false }; @@ -26,15 +27,12 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -45,7 +43,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Rc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } @@ -58,22 +56,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Arc` when T is a buffer type", "try", format!("Arc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { let Some(id) = path_def_id(cx, ty) else { return false }; if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -84,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Arc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index ecb6720053908..7883353e3fef6 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { @@ -21,7 +22,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_then( cx, @@ -47,9 +47,8 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => return false, }; - let inner_qpath = match &ty.kind { - TyKind::Path(inner_qpath) => inner_qpath, - _ => return false, + let TyKind::Path(inner_qpath) = &ty.kind else { + return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { @@ -64,7 +63,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ None => return false, }; if inner_sym == outer_sym { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability); span_lint_and_then( cx, diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 57aff5367dd15..1307288623f95 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; @@ -7,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve let fn_sig = cx.tcx.fn_sig(def_id); let generics = cx.tcx.predicates_of(def_id); let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); - let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); let partial_ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error @@ -99,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve if trait_pred.self_ty() == inp; if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); then { - if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds.iter().any(|pord| { - pord.self_ty() == return_ty_pred.term.ty().unwrap() - }) { + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { args_to_check.push((i, "PartialOrd".to_string())); } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index fb73c386640b4..b305dae76084c 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -163,9 +163,8 @@ fn unnest_or_patterns(pat: &mut P) -> bool { noop_visit_pat(p, self); // Don't have an or-pattern? Just quit early on. - let alternatives = match &mut p.kind { - Or(ps) => ps, - _ => return, + let Or(alternatives) = &mut p.kind else { + return }; // Collapse or-patterns directly nested in or-patterns. diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 8bcdff66331d1..92053cec59fc8 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,9 +47,8 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let expr = match s.kind { - hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, - _ => return, + let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { + return }; match expr.kind { diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a82643a59f97b..1f69db1cbca40 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { match e.kind { ExprKind::Match(_, arms, MatchSource::TryDesugar) => { - let e = match arms[0].body.kind { - ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, - _ => return, + let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else { + return }; if let ExprKind::Call(_, [arg, ..]) = e.kind { self.try_desugar_arm.push(arg.hir_id); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e069de8cb5c7e..0c052d86eda40 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -12,6 +12,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; declare_clippy_lint! { @@ -37,15 +38,13 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // ./tests/ui/new_lint.stdout - /// if_chain! { - /// if let ExprKind::If(ref cond, ref then, None) = item.kind, - /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, - /// if let ExprKind::Path(ref path) = left.kind, - /// if let ExprKind::Lit(ref lit) = right.kind, - /// if let LitKind::Int(42, _) = lit.node, - /// then { - /// // report your lint here - /// } + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here /// } /// ``` pub LINT_AUTHOR, @@ -91,15 +90,16 @@ macro_rules! field { }; } -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } } impl<'tcx> LateLintPass<'tcx> for Author { @@ -149,9 +149,10 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) { fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { if has_attr(cx, hir_id) { - prelude(); f(&PrintVisitor::new(cx)); - done(); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); } } @@ -195,7 +196,9 @@ struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names - ids: std::cell::Cell>, + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, } #[allow(clippy::unused_self)] @@ -203,7 +206,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - ids: std::cell::Cell::default(), + ids: Cell::default(), + first: Cell::new(true), } } @@ -226,10 +230,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { match option.value { - None => out!("if {option}.is_none();"), + None => chain!(self, "{option}.is_none()"), Some(value) => { let value = &self.bind(name, value); - out!("if let Some({value}) = {option};"); + chain!(self, "let Some({value}) = {option}"); f(value); }, } @@ -237,9 +241,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { if slice.value.is_empty() { - out!("if {slice}.is_empty();"); + chain!(self, "{slice}.is_empty()"); } else { - out!("if {slice}.len() == {};", slice.value.len()); + chain!(self, "{slice}.len() == {}", slice.value.len()); for (i, value) in slice.value.iter().enumerate() { let name = format!("{slice}[{i}]"); f(&Binding { name, value }); @@ -254,23 +258,23 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn ident(&self, ident: &Binding) { - out!("if {ident}.as_str() == {:?};", ident.value.as_str()); + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); } fn symbol(&self, symbol: &Binding) { - out!("if {symbol}.as_str() == {:?};", symbol.value.as_str()); + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { - out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));"); + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); } else { - out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value)); + chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); } } fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;"); + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -298,7 +302,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { LitKind::ByteStr(ref vec) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); - out!("if let [{:?}] = **{vec};", vec.value); + chain!(self, "let [{:?}] = **{vec}", vec.value); }, LitKind::Str(s, _) => { bind!(self, s); @@ -311,15 +315,15 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { self.pat(field!(arm.pat)); match arm.value.guard { - None => out!("if {arm}.guard.is_none();"), + None => chain!(self, "{arm}.guard.is_none()"), Some(hir::Guard::If(expr)) => { bind!(self, expr); - out!("if let Some(Guard::If({expr})) = {arm}.guard;"); + chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); self.expr(expr); }, Some(hir::Guard::IfLet(let_expr)) => { bind!(self, let_expr); - out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); self.pat(field!(let_expr.pat)); self.expr(field!(let_expr.init)); }, @@ -331,9 +335,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { bind!(self, condition, body); - out!( - "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr});" + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" ); self.expr(condition); self.expr(body); @@ -347,9 +352,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) = higher::WhileLet::hir(expr.value) { bind!(self, let_pat, let_expr, if_then); - out!( - "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr});" + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" ); self.pat(let_pat); self.expr(let_expr); @@ -359,9 +365,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { bind!(self, pat, arg, body); - out!( - "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr});" + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" ); self.pat(pat); self.expr(arg); @@ -369,7 +376,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { return; } - let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;"); + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -383,7 +390,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { // if it's a path if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); } self.expr(field!(let_expr.init)); @@ -419,7 +426,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(left); self.expr(right); }, @@ -438,7 +445,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Cast({expr}, {cast_ty})"); if let TyKind::Path(ref qpath) = cast_ty.value.kind { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); self.qpath(qpath); } self.expr(expr); @@ -485,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, fn_decl, body_id); kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); - out!("if let {ret_ty} = {fn_decl}.output;"); + chain!(self, "let {ret_ty} = {fn_decl}.output"); self.body(body_id); }, ExprKind::Yield(sub, source) => { @@ -509,7 +516,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::AssignOp(op, target, value) => { bind!(self, op, target, value); kind!("AssignOp({op}, {target}, {value})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(target); self.expr(value); }, @@ -573,10 +580,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Repeat({value}, {length})"); self.expr(value); match length.value { - ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), ArrayLen::Body(anon_const) => { bind!(self, anon_const); - out!("if let ArrayLen::Body({anon_const}) = {length};"); + chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); self.body(field!(anon_const.body)); }, } @@ -600,12 +607,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn body(&self, body_id: &Binding) { let expr = self.cx.tcx.hir().body(body_id.value).value; bind!(self, expr); - out!("let {expr} = &cx.tcx.hir().body({body_id}).value;"); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); self.expr(expr); } fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;"); + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -688,7 +695,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;"); + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 85bcbc7ad2369..71f6c9909ddda 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,1566 +1,12 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; -use clippy_utils::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::match_type; -use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, - match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, - TyKind, UnOp, -}; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{ - self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, -}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, BytePos, Span}; - -use std::borrow::{Borrow, Cow}; -use std::str; - -#[cfg(feature = "internal")] +pub mod clippy_lints_internal; +pub mod collapsible_calls; +pub mod compiler_lint_functions; +pub mod if_chain_style; +pub mod interning_defined_symbol; +pub mod invalid_paths; +pub mod lint_without_lint_pass; pub mod metadata_collector; - -declare_clippy_lint! { - /// ### What it does - /// Checks for various things we like to keep tidy in clippy. - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub CLIPPY_LINTS_INTERNAL, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_clippy_lint! { - /// ### What it does - /// Ensures every lint is associated to a `LintPass`. - /// - /// ### Why is this bad? - /// The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not - /// know the name of the lint. - /// - /// ### Known problems - /// Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub LINT_1, ... } - /// declare_lint! { pub LINT_2, ... } - /// declare_lint! { pub FORGOTTEN_LINT, ... } - /// // ... - /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); - /// // missing FORGOTTEN_LINT - /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" -} - -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" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.outer().expn_data()` and suggests to use - /// the `cx.outer_expn_data()` - /// - /// ### Why is this bad? - /// `cx.outer_expn_data()` is faster and more concise. - /// - /// ### Example - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// expr.span.ctxt().outer_expn_data() - /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" -} - -declare_clippy_lint! { - /// ### What it does - /// Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// ### Why is this bad? - /// ICE in large quantities can damage your teeth - /// - /// ### Example - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated lint without an updated description, - /// i.e. `default lint description`. - /// - /// ### Why is this bad? - /// Indicates that the lint is not finished. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } - /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints `span_lint_and_then` function calls, where the - /// closure argument has only one statement and that statement is a method - /// call to `span_suggestion`, `span_help`, `span_note` (using the same - /// span), `help` or `note`. - /// - /// These usages of `span_lint_and_then` should be replaced with one of the - /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or - /// `span_lint_and_note`. - /// - /// ### Why is this bad? - /// Using the wrapper `span_lint_and_*` functions, is more - /// convenient, readable and less error prone. - /// - /// ### Example - /// ```rust,ignore - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_suggestion( - /// expr.span, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_help(expr.span, help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.help(help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_note(expr.span, note_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.note(note_msg); - /// }); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// span_lint_and_sugg( - /// cx, - /// TEST_LINT, - /// expr.span, - /// lint_msg, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for invalid `clippy::version` attributes. - /// - /// Valid values are: - /// * "pre 1.29.0" - /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - CLIPPY_LINTS_INTERNAL, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, -} - -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { - check_invalid_clippy_version_attribute(cx, item); - - let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; - } else { - return; - } - - let field = fields - .iter() - .find(|f| f.ident.as_str() == "desc") - .expect("lints must have a description field"); - - if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), - .. - }) = field.expr.kind - { - let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - span_lint( - cx, - DEFAULT_LINT, - item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - - self.declared_lints.insert(item.ident.name, item.span); - } else if sym_str == "default deprecation note" { - span_lint( - cx, - DEFAULT_DEPRECATION_REASON, - item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), - ); - } - } - } - } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { - if !matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "impl_lint_pass" | "declare_lint_pass" - ) { - return; - } - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), - ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); - } - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { - return; - } - - for (lint_name, &lint_span) in &self.declared_lints { - // When using the `declare_tool_lint!` macro, the original `lint_span`'s - // file points to "". - // `compiletest-rs` thinks that's an error in a different file and - // just ignores it. This causes the test in compile-fail/lint_pass - // not able to capture the error. - // Therefore, we need to climb the macro expansion tree and find the - // actual span that invoked `declare_tool_lint!`: - let lint_span = lint_span.ctxt().outer_expn_data().call_site; - - if !self.registered_lints.contains(lint_name) { - span_lint( - cx, - LINT_WITHOUT_LINT_PASS, - lint_span, - &format!("the lint `{lint_name}` is not added to any `LintPass`"), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: inner, - mutbl: Mutability::Not, - }, - ) = ty.kind - { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } - } - - false -} - -fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { - if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { - return; - } - - if RustcVersion::parse(value.as_str()).is_err() { - span_lint_and_help( - cx, - INVALID_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this item has an invalid `clippy::version` attribute", - None, - "please use a valid semantic version, see `doc/adding_lints.md`", - ); - } - } else { - span_lint_and_help( - cx, - MISSING_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this lint is missing the `clippy::version` attribute or version value", - None, - "please use a `clippy::version` attribute, see `doc/adding_lints.md`", - ); - } -} - -/// This function extracts the version value of a `clippy::version` attribute if the given value has -/// one -fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - attrs.iter().find_map(|attr| { - if_chain! { - // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } - } - }) -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { - if path.segments.len() == 1 { - self.output.insert(path.segments[0].ident.name); - } - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -#[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("struct_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_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -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_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - 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}`"), - ); - } - } - } -} - -declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); - -impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} - -declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), - } - } - } - } -} - -struct AndThenSnippets<'a> { - cx: Cow<'a, str>, - lint: Cow<'a, str>, - span: Cow<'a, str>, - msg: Cow<'a, str>, -} - -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); - - AndThenSnippets { - cx: cx_snippet, - lint: lint_snippet, - span: span_snippet, - msg: msg_snippet, - } -} - -struct SpanSuggestionSnippets<'a> { - help: Cow<'a, str>, - sugg: Cow<'a, str>, - applicability: Cow<'a, str>, -} - -fn span_suggestion_snippets<'a, 'hir>( - cx: &LateContext<'_>, - span_call_args: &'hir [Expr<'hir>], -) -> SpanSuggestionSnippets<'a> { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); - - SpanSuggestionSnippets { - help: help_snippet, - sugg: sugg_snippet, - applicability: applicability_snippet, - } -} - -fn suggest_suggestion( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - span_suggestion_snippets: &SpanSuggestionSnippets<'_>, -) { - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - span_suggestion_snippets.help, - span_suggestion_snippets.sugg, - span_suggestion_snippets.applicability - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_help( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - help: &str, - with_span: bool, -) { - let option_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_help({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_note( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - note: &str, - with_span: bool, -) { - let note_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[allow(clippy::too_many_lines)] -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - enum Item { - LangItem(Symbol), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); - then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - - // Check if the target item is a diagnostic item or LangItem. - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), - // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), - false, - ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - expr.span, - msg, - |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok().map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(*item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - } - - false -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } - } -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { - for item in cx.tcx.module_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } - }; - if_chain! { - // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); - // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - }; - // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); - then { - let is_to_owned = path.last().unwrap().ends_with("string"); - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - } - // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let then_block = match then.kind { - ExprKind::Block(block, _) => block, - _ => return, - }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let if_chain_span = match if_chain_span { - None => return, - Some(span) => span, - }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then {} else { return } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} - -declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); - -impl LateLintPass<'_> for MsrvAttrImpl { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, - items, - .. - }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { - cx.tcx - .type_of(f.did) - .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } - } - } -} +pub mod msrv_attr_impl; +pub mod outer_expn_data_pass; +pub mod produce_ice; +pub mod unnecessary_def_path; diff --git a/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs new file mode 100644 index 0000000000000..da9514dd15eee --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for various things we like to keep tidy in clippy. + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { + if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(last_name) = last_name { + if *last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs new file mode 100644 index 0000000000000..d7666b77f6e96 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -0,0 +1,245 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// ### What it does + /// Lints `span_lint_and_then` function calls, where the + /// closure argument has only one statement and that statement is a method + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// ### Why is this bad? + /// Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// ### Example + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + /// ``` + pub COLLAPSIBLE_SPAN_LINT_CALLS, + internal, + "found collapsible `span_lint_and_then` calls" +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, and_then_args) = expr.kind; + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(body); + let only_expr = peel_blocks_with_stmt(body.value); + if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; + if let ExprKind::Path(..) = recv.kind; + then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + }, + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, + ), + Applicability::MachineApplicable, + ); +} diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs new file mode 100644 index 0000000000000..cacd05262a215 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, 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("struct_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_chain! { + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(fn_name.as_str()); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + 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/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs new file mode 100644 index 0000000000000..883a5c08e5c11 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::{higher, is_else_clause, is_expn_of}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; + }; + let ExprKind::Block(then_block, _) = then.kind else { return }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let Some(if_chain_span) = if_chain_span else { return }; + // check for `if a && b;` + if_chain! { + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then { + } else { + return; + } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new( + span.lo() - BytePos(3), + span.hi() + BytePos(1), + span.ctxt(), + span.parent(), + ) +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs new file mode 100644 index 0000000000000..096b601572b4d --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -0,0 +1,239 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; + +use std::borrow::Cow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. + /// + /// ### Example + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary conversion from Symbol to a string. + /// + /// ### Why is this bad? + /// It's faster use symbols directly instead of strings. + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for item in cx.tcx.module_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(&def_id) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); + } + } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::Deref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, item, [], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs new file mode 100644 index 0000000000000..25532dd4e2681 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -0,0 +1,108 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::def_path_res; +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::Item; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks the paths module for invalid paths. + /// + /// ### Why is this bad? + /// It indicates a bug in the code. + /// + /// ### Example + /// None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id()); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); + let path: Vec<&str> = path + .iter() + .map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }) + .collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if def_path_res(cx, path, None) != Res::Err { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } + } + } + } + } + } + + false +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs new file mode 100644 index 0000000000000..0dac64376b065 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -0,0 +1,342 @@ +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Ensures every lint is associated to a `LintPass`. + /// + /// ### Why is this bad? + /// The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// ### Known problems + /// Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_LINT, ... } + /// // ... + /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); + /// // missing FORGOTTEN_LINT + /// ``` + pub LINT_WITHOUT_LINT_PASS, + internal, + "declaring a lint without associating it in a LintPass" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// ### Why is this bad? + /// Indicates that the lint is not finished. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for invalid `clippy::version` attributes. + /// + /// Valid values are: + /// * "pre 1.29.0" + /// * any valid semantic version + pub INVALID_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found an invalid `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for declared clippy lints without the `clippy::version` attribute. + /// + pub MISSING_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found clippy lint without `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated deprecated lint without an updated reason, + /// i.e. `"default deprecation note"`. + /// + /// ### Why is this bad? + /// Indicates that the documentation is incomplete. + /// + /// ### Example + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// TODO + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "default deprecation note" + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// This lint has been replaced by `cooler_lint` + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "this lint has been replaced by `cooler_lint`" + /// } + /// ``` + pub DEFAULT_DEPRECATION_REASON, + internal, + "found 'default deprecation note' in a deprecated lint declaration" +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) + || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) + { + return; + } + + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + let is_lint_ref_ty = is_lint_ref_type(cx, ty); + if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + check_invalid_clippy_version_attribute(cx, item); + + let expr = &cx.tcx.hir().body(body_id).value; + let fields; + if is_lint_ref_ty { + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { + fields = struct_fields; + } else { + return; + } + } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { + fields = struct_fields; + } else { + return; + } + + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind + { + let sym_str = sym.as_str(); + if is_lint_ref_ty { + if sym_str == "default lint description" { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + + self.declared_lints.insert(item.ident.name, item.span); + } else if sym_str == "default deprecation note" { + span_lint( + cx, + DEFAULT_DEPRECATION_REASON, + item.span, + &format!("the lint `{}` has the default deprecation reason", item.ident.name), + ); + } + } + } + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + cx.tcx.hir().local_def_id( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id(), + ), + ); + collector.visit_expr(cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{lint_name}` is not added to any `LintPass`"), + ); + } + } + } +} + +pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { + if let Some(value) = extract_clippy_version_value(cx, item) { + // The `sym!` macro doesn't work as it only expects a single token. + // It's better to keep it this way and have a direct `Symbol::intern` call here. + if value == Symbol::intern("pre 1.29.0") { + return; + } + + if RustcVersion::parse(value.as_str()).is_err() { + span_lint_and_help( + cx, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this item has an invalid `clippy::version` attribute", + None, + "please use a valid semantic version, see `doc/adding_lints.md`", + ); + } + } else { + span_lint_and_help( + cx, + MISSING_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this lint is missing the `clippy::version` attribute or version value", + None, + "please use a `clippy::version` attribute, see `doc/adding_lints.md`", + ); + } +} + +/// This function extracts the version value of a `clippy::version` attribute if the given value has +/// one +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + attrs.iter().find_map(|attr| { + if_chain! { + // Identify attribute + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; + if tool_name.ident.name == sym::clippy; + if attr_name.ident.name == sym::version; + if let Some(version) = attr.value_str(); + then { Some(version) } else { None } + } + }) +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c84191bb01034..d06a616e4b30b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,7 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -532,7 +532,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { // Extract lints doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); // Format documentation correctly // split off leading `.` from lint name list and indent for correct formatting diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs new file mode 100644 index 0000000000000..1e994e3f2b171 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. + /// + pub MISSING_MSRV_ATTR_IMPL, + internal, + "checking if all necessary steps were taken when adding a MSRV to a lint" +} + +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); + +impl LateLintPass<'_> for MsrvAttrImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if_chain! { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(lint_pass_trait_ref), + self_ty, + items, + .. + }) = &item.kind; + if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); + let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); + let self_ty = hir_ty_to_ty(cx.tcx, self_ty); + if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if self_ty_def.is_struct(); + if self_ty_def.all_fields().any(|f| { + cx.tcx + .type_of(f.did) + .walk() + .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + }); + if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); + then { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs new file mode 100644 index 0000000000000..2b13fad80665c --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, method_calls, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// ### Why is this bad? + /// `cx.outer_expn_data()` is faster and more concise. + /// + /// ### Example + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// expr.span.ctxt().outer_expn_data() + /// ``` + pub OUTER_EXPN_EXPN_DATA, + internal, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let (self_arg, args) = arg_lists[1]; + if args.is_empty(); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs new file mode 100644 index 0000000000000..5899b94e16ba2 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -0,0 +1,37 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// ### Why is this bad? + /// ICE in large quantities can damage your teeth + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs new file mode 100644 index 0000000000000..4cf76f5362551 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -0,0 +1,343 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +use std::str; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub UNNECESSARY_DEF_PATH, + internal, + "using a def path when a diagnostic item or a `LangItem` is available" +} + +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[derive(Default)] +pub struct UnnecessaryDefPath { + array_def_ids: FxHashSet<(DefId, Span)>, + linted_def_ids: FxHashSet, +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + match expr.kind { + ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), + ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), + _ => {}, + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(def_id, span) in &self.array_def_ids { + if self.linted_def_ids.contains(&def_id) { + continue; + } + + let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { + ("diagnostic item", format!("sym::{sym}")) + } else if let Some(sym) = get_lang_item_name(cx, def_id) { + ("language item", format!("LangItem::{sym}")) + } else { + continue; + }; + + span_lint_and_help( + cx, + UNNECESSARY_DEF_PATH, + span, + &format!("hardcoded path to a {msg}"), + None, + &format!("convert all references to use `{sugg}`"), + ); + } + } +} + +impl UnnecessaryDefPath { + #[allow(clippy::too_many_lines)] + fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if_chain! { + if let [cx_arg, def_arg, args @ ..] = args; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + then { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); + + self.linted_def_ids.insert(def_id); + } + } + } + + fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { + let Some(path) = path_from_array(elements) else { return }; + + if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + self.array_def_ids.insert((def_id, span)); + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, + }, + ExprKind::Array(exprs) => path_from_array(exprs), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } +} + +fn path_from_array(exprs: &[Expr<'_>]) -> Option> { + exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect() +} + +// def_path_res will match field names before anything else, but for this we want to match +// inherent functions first. +fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option { + def_path_res(cx, segments, None).opt_def_id().map(|def_id| { + if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx + .def_key(def_id) + .parent + .and_then(|parent_idx| { + cx.tcx + .inherent_impls(DefId { + index: parent_idx, + krate: def_id.krate, + }) + .iter() + .find_map(|impl_id| { + cx.tcx.associated_items(*impl_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + }) + }) + .map_or(def_id, |item| item.def_id) + } else { + def_id + } + }) +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option { + if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx + .tcx + .adt_def(lang_items) + .variants() + .iter() + .nth(lang_item) + .unwrap() + .name; + Some(item_name) + } else { + None + } +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d9b22664fd25b..cd8575c90e86c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); + sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fa6766f7cfe19..07e4ef6a2fef3 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -136,17 +136,49 @@ impl Constant { (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r) - .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) - .find(|r| r.map_or(true, |o| o != Ordering::Equal)) - .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + ty::Tuple(tys) if tys.len() == l.len() => l + .iter() + .zip(r) + .zip(tys) + .map(|((li, ri), cmp_type)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + _ => None, + }, + (&Self::Vec(ref l), &Self::Vec(ref r)) => { + let cmp_type = match *cmp_type.kind() { + ty::Array(ty, _) | ty::Slice(ty) => ty, + _ => return None, + }; + iter::zip(l, r) + .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) + }, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - match Self::partial_cmp(tcx, cmp_type, lv, rv) { + match Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Array(ty, _) => ty, + _ => return None, + }, + lv, + rv, + ) { Some(Equal) => Some(ls.cmp(rs)), x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Ref(_, ty, _) => ty, + _ => return None, + }, + lb, + rb, + ), // TODO: are there any useful inter-type orderings? _ => None, } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8724a4cd651de..95b3e651e2b53 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -120,7 +120,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .expr_ty(e) .has_significant_drop(self.cx.tcx, self.cx.param_env) { - self.eagerness = Lazy; + self.eagerness = ForceNoChange; return; } }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e42fcc6569b6..052db3f3a0391 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,10 +25,12 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_typeck; +extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_mir_dataflow; extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; @@ -48,6 +50,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod macros; +pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; @@ -122,7 +125,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); } } None @@ -815,13 +818,37 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } } +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind && + seg.ident.name == sym::from + { + match arg.kind { + ExprKind::Lit(hir::Lit { + node: LitKind::Str(ref sym, _), + .. + }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Repeat(_, ArrayLen::Body(len)) => { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && + let LitKind::Int(v, _) = const_lit.node + { + return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + } + } + _ => (), + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. @@ -1739,6 +1766,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool /// ```rust,ignore /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); /// ``` +/// This function is deprecated. Use [`match_function_call_with_def_id`]. pub fn match_function_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -1756,6 +1784,22 @@ pub fn match_function_call<'tcx>( None } +pub fn match_function_call_with_def_id<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + fun_def_id: DefId, +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(fun, args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); + then { + return Some(args); + } + }; + None +} + /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 5a63c290a315f..9a682fbe604ff 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -627,7 +627,7 @@ pub enum Count<'tcx> { /// `FormatParamKind::Numbered`. Param(FormatParam<'tcx>), /// Not specified. - Implied, + Implied(Option), } impl<'tcx> Count<'tcx> { @@ -638,8 +638,10 @@ impl<'tcx> Count<'tcx> { inner: Option, values: &FormatArgsValues<'tcx>, ) -> Option { + let span = inner.map(|inner| span_from_inner(values.format_string_span, inner)); + Some(match count { - rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIs(val) => Self::Is(val, span?), rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), usage, @@ -661,12 +663,12 @@ impl<'tcx> Count<'tcx> { inner?, values, )?), - rpf::Count::CountImplied => Self::Implied, + rpf::Count::CountImplied => Self::Implied(span), }) } pub fn is_implied(self) -> bool { - matches!(self, Count::Implied) + matches!(self, Count::Implied(_)) } pub fn param(self) -> Option> { @@ -675,6 +677,14 @@ impl<'tcx> Count<'tcx> { _ => None, } } + + pub fn span(self) -> Option { + match self { + Count::Is(_, span) => Some(span), + Count::Param(param) => Some(param.span), + Count::Implied(span) => span, + } + } } /// Specification for the formatting of an argument in the format string. See @@ -738,8 +748,13 @@ impl<'tcx> FormatSpec<'tcx> { /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` pub fn is_default(&self) -> bool { - self.r#trait == sym::Display - && self.width.is_implied() + self.r#trait == sym::Display && self.is_default_for_trait() + } + + /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, + /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` + pub fn is_default_for_trait(&self) -> bool { + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.flags == 0 @@ -757,6 +772,22 @@ pub struct FormatArg<'tcx> { pub span: Span, } +impl<'tcx> FormatArg<'tcx> { + /// Span of the `:` and format specifiers + /// + /// ```ignore + /// format!("{:.}"), format!("{foo:.}") + /// ^^ ^^ + /// ``` + pub fn format_span(&self) -> Span { + let base = self.span.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) + } +} + /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs new file mode 100644 index 0000000000000..d262b335d99d3 --- /dev/null +++ b/clippy_utils/src/mir/maybe_storage_live.rs @@ -0,0 +1,52 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir; +use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +pub(super) struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Domain = BitSet; + const NAME: &'static str = "maybe_storage_live"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _trans: &mut impl GenKill, + _block: mir::BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs new file mode 100644 index 0000000000000..818e603f665e7 --- /dev/null +++ b/clippy_utils/src/mir/mod.rs @@ -0,0 +1,164 @@ +use rustc_hir::{Expr, HirId}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty::TyCtxt; + +mod maybe_storage_live; + +mod possible_borrower; +pub use possible_borrower::PossibleBorrowerMap; + +mod possible_origin; + +mod transitive_relation; + +#[derive(Clone, Debug, Default)] +pub struct LocalUsage { + /// The locations where the local is used, if any. + pub local_use_locs: Vec, + /// The locations where the local is consumed or mutated, if any. + pub local_consume_or_mutate_locs: Vec, +} + +pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { + let init = vec![ + LocalUsage { + local_use_locs: Vec::new(), + local_consume_or_mutate_locs: Vec::new(), + }; + locals.len() + ]; + + traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } + + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) +} + +struct V<'a> { + locals: &'a [Local], + location: Location, + results: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for V<'a> { + fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { + if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { + return; + } + + let local = place.local; + + for (i, self_local) in self.locals.iter().enumerate() { + if local == *self_local { + if !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { + self.results[i].local_use_locs.push(loc); + } + if matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) { + self.results[i].local_consume_or_mutate_locs.push(loc); + } + } + } + } +} + +/// Convenience wrapper around `visit_local_usage`. +pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) + .map(|mut vec| { + let LocalUsage { local_use_locs, .. } = vec.remove(0); + local_use_locs + .into_iter() + .filter(|location| !is_local_assignment(mir, local, *location)) + .count() + == 1 + }) +} + +/// Returns the `mir::Body` containing the node associated with `hir_id`. +#[allow(clippy::module_name_repetitions)] +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { + let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); + tcx.optimized_mir(body_owner_local_def_id.to_def_id()) +} + +/// Tries to determine the `Local` corresponding to `expr`, if any. +/// This function is expensive and should be used sparingly. +pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + let mir = enclosing_mir(tcx, expr.hir_id); + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) +} + +/// Returns a vector of `mir::Location` where `local` is assigned. +pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { + let mut locations = Vec::new(); + for (block, data) in mir.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = Location { block, statement_index }; + if is_local_assignment(mir, local, location) { + locations.push(location); + } + } + } + locations +} + +// `is_local_assignment` is based on `is_place_assignment`: +// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350 +fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool { + let Location { block, statement_index } = location; + let basic_block = &mir.basic_blocks[block]; + if statement_index < basic_block.statements.len() { + let statement = &basic_block.statements[statement_index]; + if let StatementKind::Assign(box (place, _)) = statement.kind { + place.as_local() == Some(local) + } else { + false + } + } else { + let terminator = basic_block.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local), + TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| { + if let InlineAsmOperand::Out { place: Some(place), .. } = operand { + place.as_local() == Some(local) + } else { + false + } + }), + _ => false, + } + } +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs new file mode 100644 index 0000000000000..25717bf3d2fee --- /dev/null +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -0,0 +1,241 @@ +use super::{ + maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, + transitive_relation::TransitiveRelation, +}; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::LateContext; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use std::ops::ControlFlow; + +/// Collects the possible borrowers of each local. +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` +/// possible borrowers of `a`. +#[allow(clippy::module_name_repetitions)] +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, + body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, + possible_origin: FxHashMap>, +} + +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, + } + } + + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: mir::Place { local: dest, .. }, + .. + } = &terminator.kind + { + // TODO add doc + // If the call returns something with lifetimes, + // let's conservatively assume the returned value contains lifetime of all the arguments. + // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. + + let mut immutable_borrowers = vec![]; + let mut mutable_borrowers = vec![]; + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { + mutable_borrowers.push(p.local); + } else { + immutable_borrowers.push(p.local); + } + }, + mir::Operand::Constant(..) => (), + } + } + + let mut mutable_variables: Vec = mutable_borrowers + .iter() + .filter_map(|r| self.possible_origin.get(r)) + .flat_map(HybridBitSet::iter) + .collect(); + + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + mutable_variables.push(*dest); + } + + for y in mutable_variables { + for x in &immutable_borrowers { + self.possible_borrower.add(*x, y); + } + for x in &mutable_borrowers { + self.possible_borrower.add(*x, y); + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + type BreakTy = (); + + fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + ControlFlow::BREAK + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + let mut visit_op = |op: &mir::Operand<'_>| match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), + mir::Operand::Constant(..) => (), + }; + + match rvalue { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +#[allow(clippy::module_name_repetitions)] +pub struct PossibleBorrowerMap<'b, 'tcx> { + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), +} + +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { + let possible_origin = { + let mut vis = PossibleOriginVisitor::new(mir); + vis.visit_body(mir); + vis.into_map(cx) + }; + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir) + .pass_name("redundant_clone") + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( + &mut self, + below: &[mir::Local], + above: &[mir::Local], + borrowed: mir::Local, + at: mir::Location, + ) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } + + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); + } + + self.bitset.0.is_empty() + } + + pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } +} diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs new file mode 100644 index 0000000000000..8e7513d740ab3 --- /dev/null +++ b/clippy_utils/src/mir/possible_origin.rs @@ -0,0 +1,59 @@ +use super::transitive_relation::TransitiveRelation; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_lint::LateContext; +use rustc_middle::mir; + +/// Collect possible borrowed for every `&mut` local. +/// For example, `_1 = &mut _2` generate _1: {_2,...} +/// Known Problems: not sure all borrowed are tracked +#[allow(clippy::module_name_repetitions)] +pub(super) struct PossibleOriginVisitor<'a, 'tcx> { + possible_origin: TransitiveRelation, + body: &'a mir::Body<'tcx>, +} + +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { + pub fn new(body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_origin: TransitiveRelation::default(), + body, + } + } + + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + map + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + // Only consider `&mut`, which can modify origin place + mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + // _2: &mut _; + // _3 = move _2 + mir::Rvalue::Use(mir::Operand::Move(borrowed)) | + // _3 = move _2 as &mut _; + mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) + => { + self.possible_origin.add(lhs, borrowed.local); + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs new file mode 100644 index 0000000000000..7fe2960739fa2 --- /dev/null +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -0,0 +1,29 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir; + +#[derive(Default)] +pub(super) struct TransitiveRelation { + relations: FxHashMap>, +} + +impl TransitiveRelation { + pub fn add(&mut self, a: mir::Local, b: mir::Local) { + self.relations.entry(a).or_default().push(b); + } + + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { + let mut seen = HybridBitSet::new_empty(domain_size); + let mut stack = vec![a]; + while let Some(u) = stack.pop() { + if let Some(edges) = self.relations.get(&u) { + for &v in edges { + if seen.insert(v) { + stack.push(v); + } + } + } + } + seen + } +} diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c67..c5dcd7b31f58e 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 13938645fc3e5..bc85147343045 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,25 +16,17 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; -pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; -pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; -pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; -/// Preferably use the diagnostic item `sym::deref_method` where possible -pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[cfg(feature = "internal")] @@ -42,30 +34,22 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"] pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; -pub const FILE: [&str; 3] = ["std", "fs", "File"]; -pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; -pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; -pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; #[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; -pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; -pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -76,13 +60,7 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; -pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; -/// Preferably use the diagnostic item `sym::Option` where possible -pub const OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; -pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; -pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -95,8 +73,6 @@ pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; -pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; -pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -119,26 +95,14 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -/// Preferably use the diagnostic item `sym::Result` where possible -pub const RESULT: [&str; 3] = ["core", "result", "Result"]; -pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; -pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; #[cfg(feature = "internal")] pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; -pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; -pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 5089987ef7200..aad7da61a8a54 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,9 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; +use crate::source::{ + snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, +}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -110,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_applicability(cx, expr.span, default, applicability); + let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) } } @@ -1052,12 +1054,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } #[cfg(test)] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f612..b8824024e6c78 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); @@ -97,7 +96,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d31..47724a2fedb00 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index cc2b3e1acec71..54c1b80c42dbf 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( "target/lintcheck/sources/{}-{}/{}", crate_name, crate_version, span.file_name - ), + ) }; Some(Self { @@ -144,16 +145,17 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -161,11 +163,11 @@ fn get(path: &str) -> Result { 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(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); - thread::sleep(Duration::from_secs(retries as u64)); + eprintln!("retrying in {retries} seconds..."); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -181,11 +183,11 @@ impl CrateSource { 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/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + 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!("{}-{}.crate.tar.gz", name, version)); + 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 @@ -205,7 +207,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +220,12 @@ impl CrateSource { 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!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,22 +258,22 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::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); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); - - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } + 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(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + 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(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -389,10 +392,7 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() @@ -422,8 +422,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -457,20 +457,16 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + std::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{}", toml_path.display(), e)); + 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_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); + 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(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -479,13 +475,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { 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 { @@ -502,16 +498,19 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { 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); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + 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(); @@ -530,13 +529,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).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!("{:0>4}, {}", count, lint)); + 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!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -573,6 +572,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool logs_modified < clippy_modified } +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -602,10 +602,10 @@ fn main() { ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if let Ok(metadata) = std::fs::metadata(shared_target_dir) { if metadata.is_dir() { println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) + std::fs::remove_dir_all(shared_target_dir) .expect("failed to remove target/lintcheck/shared_target_dir"); } } @@ -678,7 +678,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); LintcheckServer::spawn(recursive_options) }); @@ -734,8 +734,8 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + for (cratename, msg) in &ices { + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -779,7 +779,7 @@ fn read_stats_from_file(file_path: &Path) -> HashMap { fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &Vec) { let same_in_both_hashmaps = old_stats .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) .map(|(k, v)| (k.to_string(), *v)) .collect::>(); @@ -787,37 +787,37 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + 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.get::(&new_key).is_none()) + .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", 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.get::(&new_key).is_some()) + .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(&new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, 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.get::<&String>(&old_key).is_none()) + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } @@ -828,19 +828,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); - std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); - std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + std::fs::create_dir(extract_dir).unwrap_or_else(|err| { + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } @@ -863,7 +865,7 @@ fn lintcheck_test() { "lintcheck/test_sources.toml", ]; let status = std::process::Command::new("cargo") - .args(&args) + .args(args) .current_dir("..") // repo root .status(); //.output(); diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c4..49072e65192f2 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`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; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } diff --git a/rust-toolchain b/rust-toolchain index 49b13cb54e71e..748d8a317160f 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-06" +channel = "nightly-2022-10-20" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/docs.rs b/src/docs.rs index bd27bc7938f8e..c033ad294a38d 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -28,6 +28,7 @@ docs! { "approx_constant", "arithmetic_side_effects", "as_conversions", + "as_ptr_cast_mut", "as_underscore", "assertions_on_constants", "assertions_on_result_states", @@ -60,6 +61,7 @@ docs! { "cast_enum_constructor", "cast_enum_truncation", "cast_lossless", + "cast_nan_to_int", "cast_possible_truncation", "cast_possible_wrap", "cast_precision_loss", @@ -257,6 +259,7 @@ docs! { "manual_async_fn", "manual_bits", "manual_clamp", + "manual_filter", "manual_filter_map", "manual_find", "manual_find_map", @@ -313,6 +316,7 @@ docs! { "missing_panics_doc", "missing_safety_doc", "missing_spin_loop", + "missing_trait_methods", "mistyped_literal_suffixes", "mixed_case_hex_literals", "mixed_read_write_in_expression", @@ -391,6 +395,7 @@ docs! { "panic", "panic_in_result_fn", "panicking_unwrap", + "partial_pub_fields", "partialeq_ne_impl", "partialeq_to_none", "path_buf_push_overwrite", @@ -553,6 +558,7 @@ docs! { "unseparated_literal_suffix", "unsound_collection_transmute", "unused_async", + "unused_format_specs", "unused_io_amount", "unused_peekable", "unused_rounding", diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt new file mode 100644 index 0000000000000..228dde996bb2f --- /dev/null +++ b/src/docs/as_ptr_cast_mut.txt @@ -0,0 +1,19 @@ +### What it does +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 +mutability is used, making it unlikely that having it as a mutable pointer is correct. + +### Example +``` +let string = String::with_capacity(1); +let ptr = string.as_ptr() as *mut u8; +unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR +``` +Use instead: +``` +let mut string = String::with_capacity(1); +let ptr = string.as_mut_ptr(); +unsafe { ptr.write(4) }; +``` \ No newline at end of file diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt index ffac894d0c50a..1c670c7733377 100644 --- a/src/docs/box_default.txt +++ b/src/docs/box_default.txt @@ -7,12 +7,6 @@ First, it's more complex, involving two calls instead of one. Second, `Box::default()` can be faster [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). -### Known problems -The lint may miss some cases (e.g. Box::new(String::from(""))). -On the other hand, it will trigger on cases where the `default` -code comes from a macro that does something different based on -e.g. target operating system. - ### Example ``` let x: Box = Box::new(Default::default()); diff --git a/src/docs/cast_nan_to_int.txt b/src/docs/cast_nan_to_int.txt new file mode 100644 index 0000000000000..122f5da0c9218 --- /dev/null +++ b/src/docs/cast_nan_to_int.txt @@ -0,0 +1,15 @@ +### What it does +Checks for a known NaN float being cast to an integer + +### Why is this bad? +NaNs are cast into zero, so one could simply use this and make the +code more readable. The lint could also hint at a programmer error. + +### Example +``` +let _: (0.0_f32 / 0.0) as u64; +``` +Use instead: +``` +let _: = 0_u64; +``` \ No newline at end of file diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt new file mode 100644 index 0000000000000..19a4d9319d94b --- /dev/null +++ b/src/docs/manual_filter.txt @@ -0,0 +1,21 @@ +### What it does +Checks for usages of `match` which could be implemented using `filter` + +### Why is this bad? +Using the `filter` method is clearer and more concise. + +### Example +``` +match Some(0) { + Some(x) => if x % 2 == 0 { + Some(x) + } else { + None + }, + None => None, +}; +``` +Use instead: +``` +Some(0).filter(|&x| x % 2 == 0); +``` \ No newline at end of file diff --git a/src/docs/missing_trait_methods.txt b/src/docs/missing_trait_methods.txt new file mode 100644 index 0000000000000..788ad764f8c39 --- /dev/null +++ b/src/docs/missing_trait_methods.txt @@ -0,0 +1,40 @@ +### What it does +Checks if a provided method is used implicitly by a trait +implementation. A usage example would be a wrapper where every method +should perform some operation before delegating to the inner type's +implemenation. + +This lint should typically be enabled on a specific trait `impl` item +rather than globally. + +### Why is this bad? +Indicates that a method is missing. + +### Example +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } +} +``` +Use instead: +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } + + fn provided() { /* ... */ } +} +``` \ No newline at end of file diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt new file mode 100644 index 0000000000000..b529adf1547de --- /dev/null +++ b/src/docs/partial_pub_fields.txt @@ -0,0 +1,27 @@ +### What it does +Checks whether partial fields of a struct are public. + +Either make all fields of a type public, or make none of them public + +### Why is this bad? +Most types should either be: +* Abstract data types: complex objects with opaque implementation which guard +interior invariants and expose intentionally limited API to the outside world. +* Data: relatively simple objects which group a bunch of related attributes together. + +### Example +``` +pub struct Color { + pub r: u8, + pub g: u8, + b: u8, +} +``` +Use instead: +``` +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, +} +``` \ No newline at end of file diff --git a/src/docs/unused_format_specs.txt b/src/docs/unused_format_specs.txt new file mode 100644 index 0000000000000..77be3a2fb170d --- /dev/null +++ b/src/docs/unused_format_specs.txt @@ -0,0 +1,24 @@ +### What it does +Detects [formatting parameters] that have no effect on the output of +`format!()`, `println!()` or similar macros. + +### Why is this bad? +Shorter format specifiers are easier to read, it may also indicate that +an expected formatting operation such as adding padding isn't happening. + +### Example +``` +println!("{:.}", 1.0); + +println!("not padded: {:5}", format_args!("...")); +``` +Use instead: +``` +println!("{}", 1.0); + +println!("not padded: {}", format_args!("...")); +// OR +println!("padded: {:5}", format!("...")); +``` + +[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters \ No newline at end of file diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa769222d1af3..c10ee969c0146 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -283,7 +283,7 @@ fn run_ui_cargo() { env::set_current_dir(&src_path)?; let cargo_toml_path = case.path().join("Cargo.toml"); - let cargo_content = fs::read(&cargo_toml_path)?; + let cargo_content = fs::read(cargo_toml_path)?; let cargo_parsed: toml::Value = toml::from_str( std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"), ) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd9101..6d0022f7a5ccf 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162cf..07c5941013c1a 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index b823ff7fe37f0..9a9790a4bae51 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -1,5 +1,5 @@ #![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { // Good path diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 4c050332f2cc9..cbbb465230641 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Result); let _ = is_type_diagnostic_item(cx, ty, sym::Result); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 6506f1f164ac6..f17fed6c65304 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, RESULT); let _ = match_type(cx, ty, &["core", "result", "Result"]); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs new file mode 100644 index 0000000000000..b5ff3a5420561 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -0,0 +1,16 @@ +#![feature(rustc_private)] +#![allow(unused)] +#![warn(clippy::unnecessary_def_path)] + +extern crate rustc_hir; + +use rustc_hir::LangItem; + +fn main() { + const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + + // Don't lint, not yet a diagnostic or language item + const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr new file mode 100644 index 0000000000000..af46d87bf676e --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -0,0 +1,27 @@ +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::Deref` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs new file mode 100644 index 0000000000000..0d1d9258433b2 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.rs @@ -0,0 +1,37 @@ +#![allow(unused)] +#![warn(clippy::as_ptr_cast_mut)] +#![allow(clippy::wrong_self_convention)] + +struct MutPtrWrapper(Vec); +impl MutPtrWrapper { + fn as_ptr(&mut self) -> *const u8 { + self.0.as_mut_ptr() as *const u8 + } +} + +struct Covariant(*const T); +impl Covariant { + fn as_ptr(self) -> *const T { + self.0 + } +} + +fn main() { + let mut string = String::new(); + let _ = string.as_ptr() as *mut u8; + let _: *mut i8 = string.as_ptr() as *mut _; + let _ = string.as_ptr() as *const i8; + let _ = string.as_mut_ptr(); + let _ = string.as_mut_ptr() as *mut u8; + let _ = string.as_mut_ptr() as *const u8; + + let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap(); + let _ = nn.as_ptr() as *mut u8; + + let mut wrap = MutPtrWrapper(Vec::new()); + let _ = wrap.as_ptr() as *mut u8; + + let mut local = 4; + let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _); + let _ = ref_with_write_perm.as_ptr() as *mut u8; +} diff --git a/tests/ui/as_ptr_cast_mut.stderr b/tests/ui/as_ptr_cast_mut.stderr new file mode 100644 index 0000000000000..2189c3d2f8556 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.stderr @@ -0,0 +1,16 @@ +error: casting the result of `as_ptr` to *mut u8 + --> $DIR/as_ptr_cast_mut.rs:21:13 + | +LL | let _ = string.as_ptr() as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + | + = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings` + +error: casting the result of `as_ptr` to *mut i8 + --> $DIR/as_ptr_cast_mut.rs:22:22 + | +LL | let _: *mut i8 = string.as_ptr() as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index 597318a556b85..27ad538f24d8c 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Cast(expr, cast_ty) = init.kind; - if let TyKind::Path(ref qpath) = cast_ty.kind; - if match_qpath(qpath, &["char"]); - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Cast(expr, cast_ty) = init.kind + && let TyKind::Path(ref qpath) = cast_ty.kind + && match_qpath(qpath, &["char"]) + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" +{ + // report your lint here } diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index a529981e2e683..9de0550d81d00 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,64 +1,58 @@ -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 3; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Lit(ref lit) = init.kind; - if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - if let StmtKind::Local(local1) = block.stmts[1].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit1) = init1.kind; - if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind; - if name1.as_str() == "_t"; - if let StmtKind::Semi(e) = block.stmts[2].kind; - if let ExprKind::Unary(UnOp::Neg, inner) = e.kind; - if let ExprKind::Path(ref qpath) = inner.kind; - if match_qpath(qpath, &["x"]); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 3 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Local(local1) = block.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit1) = init1.kind + && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind + && name1.as_str() == "_t" + && let StmtKind::Semi(e) = block.stmts[2].kind + && let ExprKind::Unary(UnOp::Neg, inner) = e.kind + && let ExprKind::Path(ref qpath) = inner.kind + && match_qpath(qpath, &["x"]) + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["String", "new"]); - if args.is_empty(); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "expr"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Call(func1, args1) = trailing_expr.kind; - if let ExprKind::Path(ref qpath1) = func1.kind; - if match_qpath(qpath1, &["drop"]); - if args1.len() == 1; - if let ExprKind::Path(ref qpath2) = args1[0].kind; - if match_qpath(qpath2, &["expr"]); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["String", "new"]) + && args.is_empty() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "expr" + && let Some(trailing_expr) = block.expr + && let ExprKind::Call(func1, args1) = trailing_expr.kind + && let ExprKind::Path(ref qpath1) = func1.kind + && match_qpath(qpath1, &["drop"]) + && args1.len() == 1 + && let ExprKind::Path(ref qpath2) = args1[0].kind + && match_qpath(qpath2, &["expr"]) +{ + // report your lint here } -if_chain! { - if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind; - if let FnRetTy::DefaultReturn(_) = fn_decl.output; - let expr1 = &cx.tcx.hir().body(body_id).value; - if let ExprKind::Call(func, args) = expr1.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)); - if args.len() == 1; - if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind; - if let FnRetTy::DefaultReturn(_) = fn_decl1.output; - let expr2 = &cx.tcx.hir().body(body_id1).value; - if let ExprKind::Block(block, None) = expr2.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr1 = &cx.tcx.hir().body(body_id).value + && let ExprKind::Call(func, args) = expr1.kind + && let ExprKind::Path(ref qpath) = func.kind + && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && args.len() == 1 + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let FnRetTy::DefaultReturn(_) = fn_decl1.output + && expr2 = &cx.tcx.hir().body(body_id1).value + && let ExprKind::Block(block, None) = expr2.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 266312d63e50d..f040f6330a64d 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,16 +1,14 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]); - if args.len() == 2; - if let ExprKind::Lit(ref lit) = args[0].kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node; - if let ExprKind::Lit(ref lit1) = args[1].kind; - if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node; - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && args.len() == 2 + && let ExprKind::Lit(ref lit) = args[0].kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node + && let ExprKind::Lit(ref lit1) = args[1].kind + && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8d92849b3668f..5d79618820d80 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -1,50 +1,46 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::If(cond, then, Some(else_expr)) = init.kind; - if let ExprKind::DropTemps(expr) = cond.kind; - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Binary(op, left, right) = e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Lit(ref lit1) = left.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Lit(ref lit2) = right.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node; - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.len() == 1; - if let StmtKind::Semi(e1) = block1.stmts[0].kind; - if let ExprKind::Binary(op1, left1, right1) = e1.kind; - if BinOpKind::Eq == op1.node; - if let ExprKind::Lit(ref lit3) = left1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node; - if let ExprKind::Lit(ref lit4) = right1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node; - if block1.expr.is_none(); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::If(cond, then, Some(else_expr)) = init.kind + && let ExprKind::DropTemps(expr) = cond.kind + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Binary(op, left, right) = e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Lit(ref lit1) = left.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Lit(ref lit2) = right.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Binary(op1, left1, right1) = e1.kind + && BinOpKind::Eq == op1.node + && let ExprKind::Lit(ref lit3) = left1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node + && let ExprKind::Lit(ref lit4) = right1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node + && block1.expr.is_none() + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } -if_chain! { - if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind; - if let ExprKind::Let(let_expr) = cond.kind; - if let PatKind::Lit(lit_expr) = let_expr.pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.init.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if block1.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_expr) = cond.kind + && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.init.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && block1.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index bce4bc702733f..32a3127b85a3e 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["std", "mem", "transmute"]); - if args.len() == 1; - if let ExprKind::Path(ref qpath1) = args[0].kind; - if match_qpath(qpath1, &["ZPTR"]); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["std", "mem", "transmute"]) + && args.len() == 1 + && let ExprKind::Path(ref qpath1) = args[0].kind + && match_qpath(qpath1, &["ZPTR"]) + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index ceb53fcd49636..94a6436ed5479 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -1,113 +1,101 @@ -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind; - if name.as_str() == "y"; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Path(ref qpath1) = init.kind; - if match_qpath(qpath1, &["y"]); - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "z"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "y" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Path(ref qpath1) = init.kind + && match_qpath(qpath1, &["y"]) + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "z" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if let Some(label) = destination.label; - if label.ident.as_str() == "'label"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && let Some(label) = destination.label + && label.ident.as_str() == "'label" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr); - if let ExprKind::Path(ref qpath) = condition.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) + && let ExprKind::Path(ref qpath) = condition.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr); - if let PatKind::Lit(lit_expr) = let_pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) + && let PatKind::Lit(lit_expr) = let_pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = if_then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind; - if body.stmts.len() == 1; - if let StmtKind::Semi(e) = body.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if body.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind + && body.stmts.len() == 1 + && let StmtKind::Semi(e) = body.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && body.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 2cf69a035b4c7..88e2ca656a4f6 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -1,38 +1,36 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind; - if let ExprKind::Lit(ref lit) = scrutinee.kind; - if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node; - if arms.len() == 3; - if let PatKind::Lit(lit_expr) = arms[0].pat.kind; - if let ExprKind::Lit(ref lit1) = lit_expr.kind; - if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node; - if arms[0].guard.is_none(); - if let ExprKind::Lit(ref lit2) = arms[0].body.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node; - if let PatKind::Lit(lit_expr1) = arms[1].pat.kind; - if let ExprKind::Lit(ref lit3) = lit_expr1.kind; - if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node; - if arms[1].guard.is_none(); - if let ExprKind::Block(block, None) = arms[1].body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local1) = block.stmts[0].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit4) = init1.kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind; - if name.as_str() == "x"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Path(ref qpath) = trailing_expr.kind; - if match_qpath(qpath, &["x"]); - if let PatKind::Wild = arms[2].pat.kind; - if arms[2].guard.is_none(); - if let ExprKind::Lit(ref lit5) = arms[2].body.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "a"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind + && let ExprKind::Lit(ref lit) = scrutinee.kind + && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node + && arms.len() == 3 + && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node + && arms[0].guard.is_none() + && let ExprKind::Lit(ref lit2) = arms[0].body.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node + && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node + && arms[1].guard.is_none() + && let ExprKind::Block(block, None) = arms[1].body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local1) = block.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit4) = init1.kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind + && name.as_str() == "x" + && let Some(trailing_expr) = block.expr + && let ExprKind::Path(ref qpath) = trailing_expr.kind + && match_qpath(qpath, &["x"]) + && let PatKind::Wild = arms[2].pat.kind + && arms[2].guard.is_none() + && let ExprKind::Lit(ref lit5) = arms[2].body.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "a" +{ + // report your lint here } diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index 471bbce4f4185..c2a369610cc1b 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -1,12 +1,10 @@ -if_chain! { - if let ExprKind::Repeat(value, length) = expr.kind; - if let ExprKind::Lit(ref lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - if let ArrayLen::Body(anon_const) = length; - let expr1 = &cx.tcx.hir().body(anon_const.body).value; - if let ExprKind::Lit(ref lit1) = expr1.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; - then { - // report your lint here - } +if let ExprKind::Repeat(value, length) = expr.kind + && let ExprKind::Lit(ref lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node + && let ArrayLen::Body(anon_const) = length + && expr1 = &cx.tcx.hir().body(anon_const.body).value + && let ExprKind::Lit(ref lit1) = expr1.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node +{ + // report your lint here } diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b5bbc9e213c6e..0b332d5e7d0e1 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,64 +1,56 @@ -if_chain! { - if let ExprKind::Struct(qpath, fields, None) = expr.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let ExprKind::Lit(ref lit) = expr1.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = block.expr; - if let ExprKind::Lit(ref lit1) = trailing_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if let Some(trailing_expr1) = block1.expr; - if let ExprKind::Lit(ref lit2) = trailing_expr1.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node; - then { - // report your lint here - } +if let ExprKind::Struct(qpath, fields, None) = expr.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let ExprKind::Lit(ref lit) = expr1.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && let Some(trailing_expr) = block.expr + && let ExprKind::Lit(ref lit1) = trailing_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && let Some(trailing_expr1) = block1.expr + && let ExprKind::Lit(ref lit2) = trailing_expr1.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node +{ + // report your lint here } -if_chain! { - if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let PatKind::Lit(lit_expr) = fields[0].pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind; - if match_qpath(qpath, &["TestTuple"]); - if fields.len() == 1; - if let PatKind::Lit(lit_expr) = fields[0].kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind + && match_qpath(qpath, &["TestTuple"]) + && fields.len() == 1 + && let PatKind::Lit(lit_expr) = fields[0].kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind; - if method_name.ident.as_str() == "test"; - if let ExprKind::Path(ref qpath) = receiver.kind; - if match_qpath(qpath, &["test_method_call"]); - if args.is_empty(); - then { - // report your lint here - } +if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind + && method_name.ident.as_str() == "test" + && let ExprKind::Path(ref qpath) = receiver.kind + && match_qpath(qpath, &["test_method_call"]) + && args.is_empty() +{ + // report your lint here } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed new file mode 100644 index 0000000000000..911fa856aa0a3 --- /dev/null +++ b/tests/ui/box_default.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::default(); + let _byte = Box::::default(); + let _vec = Box::>::default(); + let _impl = Box::::default(); + let _impl2 = Box::::default(); + let _impl3: Box = Box::default(); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); + let _vec2: Box> = Box::default(); + let _vec3: Box> = Box::default(); + let _vec4: Box<_> = Box::>::default(); + let _more = ret_ty_fn(); + call_ty_fn(Box::default()); +} + +fn ret_ty_fn() -> Box { + Box::::default() +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::::default(); +} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index dc522705bc624..20019c2ee5a07 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::box_default)] #[derive(Default)] @@ -26,6 +27,31 @@ fn main() { let _impl3: Box = Box::new(Default::default()); let _own = Box::new(OwnDefault::default()); // should not lint let _in_macro = outer!(Box::new(String::new())); - // false negative: default is from different expansion + let _string_default = outer!(Box::new(String::from(""))); let _vec2: Box> = Box::new(vec![]); + let _vec3: Box> = Box::new(Vec::from([])); + let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + let _more = ret_ty_fn(); + call_ty_fn(Box::new(u8::default())); +} + +fn ret_ty_fn() -> Box { + Box::new(bool::default()) +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::new(ImplementsDefault::default()); } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index b2030e95acb10..5ea410331afb3 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -1,59 +1,88 @@ error: `Box::new(_)` of default value - --> $DIR/box_default.rs:21:32 + --> $DIR/box_default.rs:22:32 | LL | let _string: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | - = help: use `Box::default()` instead = note: `-D clippy::box-default` implied by `-D warnings` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:22:17 + --> $DIR/box_default.rs:23:17 | LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:23:16 + --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:24:17 + --> $DIR/box_default.rs:25:17 | LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:25:18 + --> $DIR/box_default.rs:26:18 | LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:26:42 + --> $DIR/box_default.rs:27:42 | LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:28:28 + --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:30:34 + | +LL | let _string_default = outer!(Box::new(String::from(""))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:31:46 + | +LL | let _vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:32:33 + | +LL | let _vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:33:25 + | +LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:35:16 + | +LL | call_ty_fn(Box::new(u8::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:39:5 + | +LL | Box::new(bool::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:56:28 | - = help: use `Box::default()` instead +LL | let _: Box = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 7 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs new file mode 100644 index 0000000000000..4326abc9a5410 --- /dev/null +++ b/tests/ui/box_default_no_std.rs @@ -0,0 +1,33 @@ +#![feature(lang_items, start, libc)] +#![warn(clippy::box_default)] +#![no_std] + +pub struct NotBox { + _value: T, +} + +impl NotBox { + pub fn new(value: T) -> Self { + Self { _value: value } + } +} + +impl Default for NotBox { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _p = NotBox::new(isize::default()); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1e..e6bf944c7a5ec 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a05..c87320b5209db 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d23..1b39c554b0384 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:9:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:14:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:15:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:23:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:33:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:47:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c37858..af13b755e310a 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f95..3b06af899c60d 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d5..768b033d10a28 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:8:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,76 +7,82 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:14:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:15:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:21:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:23:13 + --> $DIR/cast_lossless_bool.rs:24:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:54:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cast_nan_to_int.rs b/tests/ui/cast_nan_to_int.rs new file mode 100644 index 0000000000000..287c5aa216bd3 --- /dev/null +++ b/tests/ui/cast_nan_to_int.rs @@ -0,0 +1,18 @@ +#![warn(clippy::cast_nan_to_int)] +#![allow(clippy::eq_op)] + +fn main() { + let _ = (0.0_f32 / -0.0) as usize; + let _ = (f64::INFINITY * -0.0) as usize; + let _ = (0.0 * f32::INFINITY) as usize; + + let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::INFINITY) as usize; + let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + + // those won't be linted: + let _ = (1.0_f32 / 0.0) as usize; + let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize; + let _ = (f64::INFINITY - 0.0) as usize; +} diff --git a/tests/ui/cast_nan_to_int.stderr b/tests/ui/cast_nan_to_int.stderr new file mode 100644 index 0000000000000..3539be75a19db --- /dev/null +++ b/tests/ui/cast_nan_to_int.stderr @@ -0,0 +1,51 @@ +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:5:13 + | +LL | let _ = (0.0_f32 / -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + = note: `-D clippy::cast-nan-to-int` implied by `-D warnings` + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:6:13 + | +LL | let _ = (f64::INFINITY * -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:7:13 + | +LL | let _ = (0.0 * f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:9:13 + | +LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:10:13 + | +LL | let _ = (f32::INFINITY - f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:11:13 + | +LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef8..8a5645b22ed19 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85be..2fb140efae768 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b0..08df7b2b39a0c 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:43:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efae..f936957cb40c6 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a5..77aec713ff316 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c4..b2bf7af8daf87 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:16:13 + --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:20:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:21:13 + --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:25:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:26:13 + --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:32:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:33:13 + --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:37:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:38:13 + --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:44:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:45:13 + --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:49:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:50:13 + --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:54:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:55:13 + --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: aborting due to 16 previous errors +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:92:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64e..42ed232d1001c 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb5..471bd9654cc13 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d3214689..914c9a91e8300 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:9:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:7:31 + --> $DIR/cloned_instead_of_copied.rs:10:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:8:22 + --> $DIR/cloned_instead_of_copied.rs:11:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:34 + --> $DIR/cloned_instead_of_copied.rs:12:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:32 + --> $DIR/cloned_instead_of_copied.rs:13:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:31:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:37:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:38:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d30..1d7a72846419f 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091e..0294be60b43fd 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors diff --git a/tests/ui/crashes/ice-9625.rs b/tests/ui/crashes/ice-9625.rs new file mode 100644 index 0000000000000..a765882b5d818 --- /dev/null +++ b/tests/ui/crashes/ice-9625.rs @@ -0,0 +1,4 @@ +fn main() { + let x = &1; + let _ = &1 < x && x < &10; +} diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a28bff76755b6..a370ccc76962e 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.0_f64; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index b48435cc7b282..2476fe95141de 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8b6c7746edbb..5df2f642388dc 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -61,79 +61,85 @@ LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:43:21 + --> $DIR/default_numeric_fallback_f64.rs:44:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:51:21 + --> $DIR/default_numeric_fallback_f64.rs:52:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:57:21 + --> $DIR/default_numeric_fallback_f64.rs:58:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:69:9 + --> $DIR/default_numeric_fallback_f64.rs:66:21 + | +LL | let y = 1.; + | ^^ help: consider adding suffix: `1.0_f64` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_f64.rs:78:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:75:27 + --> $DIR/default_numeric_fallback_f64.rs:84:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:79:29 + --> $DIR/default_numeric_fallback_f64.rs:88:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:21 + --> $DIR/default_numeric_fallback_f64.rs:102:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:96:32 + --> $DIR/default_numeric_fallback_f64.rs:105:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:28 + --> $DIR/default_numeric_fallback_f64.rs:123:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:117:36 + --> $DIR/default_numeric_fallback_f64.rs:126:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:135:24 + --> $DIR/default_numeric_fallback_f64.rs:144:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:155:23 + --> $DIR/default_numeric_fallback_f64.rs:164:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:162:21 + --> $DIR/default_numeric_fallback_f64.rs:171:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` @@ -143,5 +149,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55451cf2f7d0f..3f4994f0453b1 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1_i32; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 62d72f2febaaa..2df0e09787f90 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index f7c5e724c403c..6f219c3fc2b0e 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -73,79 +73,85 @@ LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:43:21 + --> $DIR/default_numeric_fallback_i32.rs:45:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:51:21 + --> $DIR/default_numeric_fallback_i32.rs:53:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:57:21 + --> $DIR/default_numeric_fallback_i32.rs:59:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:69:9 + --> $DIR/default_numeric_fallback_i32.rs:67:21 + | +LL | let y = 1; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:79:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:27 + --> $DIR/default_numeric_fallback_i32.rs:85:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:29 + --> $DIR/default_numeric_fallback_i32.rs:89:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:93:21 + --> $DIR/default_numeric_fallback_i32.rs:103:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:96:32 + --> $DIR/default_numeric_fallback_i32.rs:106:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:114:28 + --> $DIR/default_numeric_fallback_i32.rs:124:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:117:36 + --> $DIR/default_numeric_fallback_i32.rs:127:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:135:24 + --> $DIR/default_numeric_fallback_i32.rs:145:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:155:23 + --> $DIR/default_numeric_fallback_i32.rs:165:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:162:21 + --> $DIR/default_numeric_fallback_i32.rs:172:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -155,5 +161,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae400..3bac738acd65b 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c98..6e7c47d9ad3cf 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c09..91a6cf8de65fc 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:13:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` | = note: `-D clippy::err-expect` implied by `-D warnings` -error: aborting due to previous error +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:30:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf3..41828ddd7acde 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a968391..be492a81b45ec 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a32..e789efeabd550 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:8:32 + --> $DIR/filter_map_next_fixable.rs:10:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to previous error +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:25:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index 24cf0847dd588..825e122be5a5f 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller())); + print!("{}", ((Location::caller()))); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index 753babf0afdc7..a41e53389e52a 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller().to_string())); + print!("{}", ((Location::caller()).to_string())); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index 68b0bb9e089e1..f1832b970198a 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:76:72 + --> $DIR/format_args.rs:77:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,136 +7,148 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:80:27 + --> $DIR/format_args.rs:81:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:85:27 + --> $DIR/format_args.rs:86:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:87:63 + --> $DIR/format_args.rs:88:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:88:65 + --> $DIR/format_args.rs:89:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:89:64 + --> $DIR/format_args.rs:90:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:90:66 + --> $DIR/format_args.rs:91:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:91:77 + --> $DIR/format_args.rs:92:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:92:70 + --> $DIR/format_args.rs:93:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:93:73 + --> $DIR/format_args.rs:94:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:94:73 + --> $DIR/format_args.rs:95:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:95:63 + --> $DIR/format_args.rs:96:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:98:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:24 + --> $DIR/format_args.rs:99:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:99:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:20 + --> $DIR/format_args.rs:101:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:103:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:104:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:104:39 + --> $DIR/format_args.rs:105:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:105:52 + --> $DIR/format_args.rs:106:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:118:37 + | +LL | print!("{}", (Location::caller().to_string())); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:119:39 + | +LL | print!("{}", ((Location::caller()).to_string())); + | ^^^^^^^^^^^^ help: remove this + error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:144:38 + --> $DIR/format_args.rs:147:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:158:24 + --> $DIR/format_args.rs:161:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed new file mode 100644 index 0000000000000..1cf49ca45f494 --- /dev/null +++ b/tests/ui/from_over_into.fixed @@ -0,0 +1,87 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::from_over_into)] +#![allow(unused)] + +// this should throw an error +struct StringWrapper(String); + +impl From for StringWrapper { + fn from(val: String) -> Self { + StringWrapper(val) + } +} + +struct SelfType(String); + +impl From for SelfType { + fn from(val: String) -> Self { + SelfType(String::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl From for SelfKeywords { + fn from(val: X) -> Self { + let _ = X::default(); + let _ = X::FOO; + let _: X = val; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::From for bool { + fn from(mut val: crate::ExplicitPaths) -> Self { + let in_closure = || val.0; + + val.0 = false; + val.0 + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 292d0924fb17a..d30f3c3fc9256 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,4 +1,8 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] +#![allow(unused)] // this should throw an error struct StringWrapper(String); @@ -9,6 +13,44 @@ impl Into for String { } } +struct SelfType(String); + +impl Into for String { + fn into(self) -> SelfType { + SelfType(Self::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl Into for X { + fn into(self) -> SelfKeywords { + let _ = Self::default(); + let _ = Self::FOO; + let _: Self = self; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::Into for crate::ExplicitPaths { + fn into(mut self) -> bool { + let in_closure = || self.0; + + self.0 = false; + self.0 + } +} + // this is fine struct A(String); @@ -18,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 469adadd2196d..9c2a7c04c3646 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,11 +1,75 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:6:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider to implement `From` instead = note: `-D clippy::from-over-into` implied by `-D warnings` +help: replace the `Into` implentation with `From` + | +LL ~ impl From for StringWrapper { +LL ~ fn from(val: String) -> Self { +LL ~ StringWrapper(val) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:18:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfType { +LL ~ fn from(val: String) -> Self { +LL ~ SelfType(String::new()) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:33:1 + | +LL | impl Into for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfKeywords { +LL ~ fn from(val: X) -> Self { +LL ~ let _ = X::default(); +LL ~ let _ = X::FOO; +LL ~ let _: X = val; + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:45:1 + | +LL | impl core::convert::Into for crate::ExplicitPaths { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implentation with `From` + | +LL ~ impl core::convert::From for bool { +LL ~ fn from(mut val: crate::ExplicitPaths) -> Self { +LL ~ let in_closure = || val.0; +LL | +LL ~ val.0 = false; +LL ~ val.0 + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:80:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | -error: aborting due to previous error +error: aborting due to 5 previous errors diff --git a/tests/ui/from_over_into_unfixable.rs b/tests/ui/from_over_into_unfixable.rs new file mode 100644 index 0000000000000..3b280b7488ae7 --- /dev/null +++ b/tests/ui/from_over_into_unfixable.rs @@ -0,0 +1,35 @@ +#![warn(clippy::from_over_into)] + +struct InMacro(String); + +macro_rules! in_macro { + ($e:ident) => { + $e + }; +} + +impl Into for String { + fn into(self) -> InMacro { + InMacro(in_macro!(self)) + } +} + +struct WeirdUpperSelf; + +impl Into for &'static [u8] { + fn into(self) -> WeirdUpperSelf { + let _ = Self::default(); + WeirdUpperSelf + } +} + +struct ContainsVal; + +impl Into for ContainsVal { + fn into(self) -> u8 { + let val = 1; + val + 1 + } +} + +fn main() {} diff --git a/tests/ui/from_over_into_unfixable.stderr b/tests/ui/from_over_into_unfixable.stderr new file mode 100644 index 0000000000000..6f6ce351921be --- /dev/null +++ b/tests/ui/from_over_into_unfixable.stderr @@ -0,0 +1,29 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:11:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From` + = note: `-D clippy::from-over-into` implied by `-D warnings` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:19:1 + | +LL | impl Into for &'static [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From<&'static [u8]>` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:28:1 + | +LL | impl Into for ContainsVal { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence + = help: replace the `Into` implentation with `From` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index e6f57e9267eac..93df81b1a7ff0 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -165,4 +180,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 8bb28d149c628..8340bc8264d58 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -211,4 +226,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 5bb9a606422a1..5e589d931e431 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,5 +1,5 @@ error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:13:5 + --> $DIR/implicit_saturating_sub.rs:28:5 | LL | / if u_8 > 0 { LL | | u_8 = u_8 - 1; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:20:13 + --> $DIR/implicit_saturating_sub.rs:35:13 | LL | / if u_8 > 0 { LL | | u_8 -= 1; @@ -17,7 +17,7 @@ LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:34:5 + --> $DIR/implicit_saturating_sub.rs:49:5 | LL | / if u_16 > 0 { LL | | u_16 -= 1; @@ -25,7 +25,7 @@ LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:44:5 + --> $DIR/implicit_saturating_sub.rs:59:5 | LL | / if u_32 != 0 { LL | | u_32 -= 1; @@ -33,7 +33,7 @@ LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:65:5 + --> $DIR/implicit_saturating_sub.rs:80:5 | LL | / if u_64 > 0 { LL | | u_64 -= 1; @@ -41,7 +41,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:70:5 + --> $DIR/implicit_saturating_sub.rs:85:5 | LL | / if 0 < u_64 { LL | | u_64 -= 1; @@ -49,7 +49,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:75:5 + --> $DIR/implicit_saturating_sub.rs:90:5 | LL | / if 0 != u_64 { LL | | u_64 -= 1; @@ -57,7 +57,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:96:5 + --> $DIR/implicit_saturating_sub.rs:111:5 | LL | / if u_usize > 0 { LL | | u_usize -= 1; @@ -65,7 +65,7 @@ LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:108:5 + --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:113:5 + --> $DIR/implicit_saturating_sub.rs:128:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -81,7 +81,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:118:5 + --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -89,7 +89,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:123:5 + --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -97,7 +97,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:133:5 + --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -105,7 +105,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:138:5 + --> $DIR/implicit_saturating_sub.rs:153:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -113,7 +113,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:143:5 + --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -121,7 +121,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:148:5 + --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -129,7 +129,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:158:5 + --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -137,7 +137,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:163:5 + --> $DIR/implicit_saturating_sub.rs:178:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -145,7 +145,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:168:5 + --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -153,7 +153,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:173:5 + --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -161,7 +161,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:183:5 + --> $DIR/implicit_saturating_sub.rs:198:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; @@ -169,7 +169,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:188:5 + --> $DIR/implicit_saturating_sub.rs:203:5 | LL | / if i64::MIN != i_64 { LL | | i_64 -= 1; @@ -177,7 +177,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:193:5 + --> $DIR/implicit_saturating_sub.rs:208:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 0cadd5a3da198..1a646e49ce3ae 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -40,3 +40,10 @@ fn main() { let ok26 = 0x6_A0_BF; let ok27 = 0b1_0010_0101; } + +fn issue9651() { + // lint but octal form is not possible here + let _ = 08; + let _ = 09; + let _ = 089; +} diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 365b240747352..603d47bacca80 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -135,5 +135,38 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: aborting due to 18 previous errors +error: this is a decimal constant + --> $DIR/literals.rs:46:13 + | +LL | let _ = 08; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 8; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:47:13 + | +LL | let _ = 09; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 9; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:48:13 + | +LL | let _ = 089; + | ^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 89; + | ~~ + +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 26e3b8f63e700..c9a819ba53544 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] @@ -29,7 +29,9 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); + if !a.is_empty() { + panic!("qwqwq"); + } if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -44,21 +46,32 @@ fn main() { 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"); + if b.is_empty() { + panic!("panic1"); + } + if b.is_empty() && a.is_empty() { + panic!("panic2"); + } + if a.is_empty() && !b.is_empty() { + panic!("panic3"); + } + if b.is_empty() || a.is_empty() { + panic!("panic4"); + } + if a.is_empty() || !b.is_empty() { + panic!("panic5"); + } assert!(!a.is_empty(), "with expansion {}", one!()); } fn issue7730(a: u8) { // Suggestion should preserve comment - // comment -/* this is a + if a > 2 { + // comment + /* this is a multiline comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 237638ee1344c..1f2e1e3087bd0 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -8,54 +8,6 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:34:5 - | -LL | / if !a.is_empty() { -LL | | panic!("qwqwq"); -LL | | } - | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:51:5 - | -LL | / if b.is_empty() { -LL | | panic!("panic1"); -LL | | } - | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:54:5 - | -LL | / if b.is_empty() && a.is_empty() { -LL | | panic!("panic2"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:57:5 - | -LL | / if a.is_empty() && !b.is_empty() { -LL | | panic!("panic3"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:60:5 - | -LL | / if b.is_empty() || a.is_empty() { -LL | | panic!("panic4"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:63:5 - | -LL | / if a.is_empty() || !b.is_empty() { -LL | | panic!("panic5"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` - error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | @@ -64,22 +16,5 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 - | -LL | / if a > 2 { -LL | | // comment -LL | | /* this is a -LL | | multiline -... | -LL | | panic!("panic with comment") // comment after `panic!` -LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a > 2), "panic with comment"); - | - -error: aborting due to 9 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 26e3b8f63e700..2f62de51cadcd 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 8c37753071dfb..6a4cc2468d419 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99fa..331fd29b74e8e 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -302,3 +303,29 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +fn msrv_1_49() { + #![clippy::msrv = "1.49"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f3..70abe28091c94 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:76:5 + --> $DIR/manual_clamp.rs:77:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:91:5 + --> $DIR/manual_clamp.rs:92:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:99:5 + --> $DIR/manual_clamp.rs:100:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:107:5 + --> $DIR/manual_clamp.rs:108:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:161:5 + --> $DIR/manual_clamp.rs:162:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:21:14 + --> $DIR/manual_clamp.rs:22:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:29:14 + --> $DIR/manual_clamp.rs:30:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:37:14 + --> $DIR/manual_clamp.rs:38:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:45:14 + --> $DIR/manual_clamp.rs:46:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:53:14 + --> $DIR/manual_clamp.rs:54:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:55:14 + --> $DIR/manual_clamp.rs:56:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:57:14 + --> $DIR/manual_clamp.rs:58:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:63:14 + --> $DIR/manual_clamp.rs:64:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:69:14 + --> $DIR/manual_clamp.rs:70:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:83:15 + --> $DIR/manual_clamp.rs:84:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:114:15 + --> $DIR/manual_clamp.rs:115:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:123:19 + --> $DIR/manual_clamp.rs:124:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:134:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:142:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:143:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:151:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:153:5 + --> $DIR/manual_clamp.rs:154:5 | LL | / if x32 < min { LL | | x32 = min; @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:324:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed new file mode 100644 index 0000000000000..3553291b87df6 --- /dev/null +++ b/tests/ui/manual_filter.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + Some(0).filter(|&x| x <= 0); + + Some(1).filter(|&x| x <= 0); + + Some(2).filter(|&x| x <= 0); + + Some(3).filter(|&x| x > 0); + + let y = Some(4); + y.filter(|&x| x <= 0); + + Some(5).filter(|&x| x > 0); + + Some(6).as_ref().filter(|&x| x > &0); + + let external_cond = true; + Some(String::new()).filter(|x| external_cond); + + Some(7).filter(|&x| external_cond); + + Some(8).filter(|&x| x != 0); + + Some(9).filter(|&x| x > 10 && x < 100); + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + Some(11).filter(|&x| { + println!("foo"); + x > 10 && x < 100 + }); + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = Some(14).filter(|&x| unsafe { f(x) }); + let _ = Some(15).filter(|&x| unsafe { f(x) }); + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else { Some(16).filter(|&x| x % 2 == 0) }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs new file mode 100644 index 0000000000000..aa9f90f752b17 --- /dev/null +++ b/tests/ui/manual_filter.rs @@ -0,0 +1,243 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + match Some(0) { + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(1) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + None => None, + }; + + match Some(2) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + _ => None, + }; + + match Some(3) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + None => None, + }; + + let y = Some(4); + match y { + // Some(4) + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(5) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(6) { + Some(ref x) => { + if x > &0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + let external_cond = true; + match Some(String::new()) { + Some(x) => { + if external_cond { + Some(x) + } else { + None + } + }, + _ => None, + }; + + if let Some(x) = Some(7) { + if external_cond { Some(x) } else { None } + } else { + None + }; + + match &Some(8) { + &Some(x) => { + if x != 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(9) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + match Some(11) { + // Lint, statement is preserved by `.filter` + Some(x) => { + if { + println!("foo"); + x > 10 && x < 100 + } { + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = match Some(14) { + Some(x) => { + if unsafe { f(x) } { + Some(x) + } else { + None + } + }, + None => None, + }; + let _ = match Some(15) { + Some(x) => unsafe { + if f(x) { Some(x) } else { None } + }, + None => None, + }; + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else if let Some(x) = Some(16) { + // Lint starting from here + if x % 2 == 0 { Some(x) } else { None } + } else { + None + }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr new file mode 100644 index 0000000000000..53dea9229306b --- /dev/null +++ b/tests/ui/manual_filter.stderr @@ -0,0 +1,191 @@ +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:7:5 + | +LL | / match Some(0) { +LL | | None => None, +LL | | Some(x) => { +LL | | if x > 0 { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | + = note: `-D clippy::manual-filter` implied by `-D warnings` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:29:5 + | +LL | / match Some(2) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:40:5 + | +LL | / match Some(3) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:52:5 + | +LL | / match y { +LL | | // Some(4) +LL | | None => None, +LL | | Some(x) => { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `y.filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:64:5 + | +LL | / match Some(5) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:75:5 + | +LL | / match Some(6) { +LL | | Some(ref x) => { +LL | | if x > &0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:87:5 + | +LL | / match Some(String::new()) { +LL | | Some(x) => { +LL | | if external_cond { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:98:5 + | +LL | / if let Some(x) = Some(7) { +LL | | if external_cond { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:104:5 + | +LL | / match &Some(8) { +LL | | &Some(x) => { +LL | | if x != 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:115:5 + | +LL | / match Some(9) { +LL | | Some(x) => { +LL | | if x > 10 && x < 100 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:141:5 + | +LL | / match Some(11) { +LL | | // Lint, statement is preserved by `.filter` +LL | | Some(x) => { +LL | | if { +... | +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ Some(11).filter(|&x| { +LL + println!("foo"); +LL + x > 10 && x < 100 +LL ~ }); + | + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:185:13 + | +LL | let _ = match Some(14) { + | _____________^ +LL | | Some(x) => { +LL | | if unsafe { f(x) } { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:195:13 + | +LL | let _ = match Some(15) { + | _____________^ +LL | | Some(x) => unsafe { +LL | | if f(x) { Some(x) } else { None } +LL | | }, +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:205:12 + | +LL | } else if let Some(x) = Some(16) { + | ____________^ +LL | | // Lint starting from here +LL | | if x % 2 == 0 { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + +error: aborting due to 15 previous errors + diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b28..b942fbfe93056 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73c..7462d532169f6 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1e..d51bac03b565a 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,16 +42,28 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:69:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:84:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e37..85009d78558ba 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -64,3 +65,21 @@ fn main() { s4[2..].to_string(); } } + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5d..ad2a362f3e76d 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:7:24 + --> $DIR/manual_strip.rs:8:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:6:5 + --> $DIR/manual_strip.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:15:24 + --> $DIR/manual_strip.rs:16:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:14:5 + --> $DIR/manual_strip.rs:15:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:24:24 + --> $DIR/manual_strip.rs:25:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:23:5 + --> $DIR/manual_strip.rs:24:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:31:24 + --> $DIR/manual_strip.rs:32:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:30:5 + --> $DIR/manual_strip.rs:31:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:37:24 + --> $DIR/manual_strip.rs:38:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:36:5 + --> $DIR/manual_strip.rs:37:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:44:24 + --> $DIR/manual_strip.rs:45:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:43:5 + --> $DIR/manual_strip.rs:44:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:50:9 + --> $DIR/manual_strip.rs:51:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:49:5 + --> $DIR/manual_strip.rs:50:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:83:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:82:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e3..396b22a9abb39 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,8 @@ // aux-build:option_helpers.rs + +#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +81,19 @@ fn main() { option_methods(); result_methods(); } + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327a..d17d24a403ea5 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:16:13 + --> $DIR/map_unwrap_or.rs:18:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:22:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap_or.rs:26:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:29:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:33:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap_or.rs:37:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:48:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:52:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap_or.rs:56:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:66:13 + --> $DIR/map_unwrap_or.rs:68:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:70:13 + --> $DIR/map_unwrap_or.rs:72:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:98:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bfb..2498007694c56 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -193,3 +194,18 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc4..b4e48499bd0fb 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -234,3 +235,21 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448b..f1d1c23aeb0de 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:10:14 + --> $DIR/match_expr_like_matches_macro.rs:11:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:17:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:28:15 + --> $DIR/match_expr_like_matches_macro.rs:29:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:16 + --> $DIR/match_expr_like_matches_macro.rs:35:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:59:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:69:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:79:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:139:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:148:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:165:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:179:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:191:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:251:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 22b04b208f87b..b4097fa96045e 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] - #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index a72becbeb669b..b98d4799e42ca 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,96 +1,96 @@ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:13:9 + --> $DIR/match_overlapping_arm.rs:12:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:14:9 + --> $DIR/match_overlapping_arm.rs:13:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:19:9 + --> $DIR/match_overlapping_arm.rs:18:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:21:9 + --> $DIR/match_overlapping_arm.rs:20:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:56:9 + --> $DIR/match_overlapping_arm.rs:55:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:57:9 + --> $DIR/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:81:9 + --> $DIR/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:80:9 + --> $DIR/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:86:9 + --> $DIR/match_overlapping_arm.rs:85:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:87:9 + --> $DIR/match_overlapping_arm.rs:86:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:98:9 + --> $DIR/match_overlapping_arm.rs:97:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:99:9 + --> $DIR/match_overlapping_arm.rs:98:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:107:9 + --> $DIR/match_overlapping_arm.rs:106:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:108:9 + --> $DIR/match_overlapping_arm.rs:107:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:121:9 + --> $DIR/match_overlapping_arm.rs:120:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:122:9 + --> $DIR/match_overlapping_arm.rs:121:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 951f552eb32b6..a6e315e4773a2 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -124,3 +124,12 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || { + side_effects(); + println!("Needs curlies"); + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 19c0fee8fd688..cecbd703e5669 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -140,3 +140,11 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || match side_effects() { + _ => println!("Needs curlies"), + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 5d4e7314b2137..2b9ec7ee7026e 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -196,5 +196,22 @@ LL + suf LL ~ }; | -error: aborting due to 13 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding.rs:147:16 + | +LL | let _ = || match side_effects() { + | ________________^ +LL | | _ => println!("Needs curlies"), +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ let _ = || { +LL + side_effects(); +LL + println!("Needs curlies"); +LL ~ }; + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_wild_err_arm.edition2021.stderr b/tests/ui/match_wild_err_arm.edition2021.stderr deleted file mode 100644 index 525533bf07bb0..0000000000000 --- a/tests/ui/match_wild_err_arm.edition2021.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 - | -LL | Err(_) => panic!("err"), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 - | -LL | Err(_) => panic!(), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 - | -LL | Err(_) => { - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 - | -LL | Err(_e) => panic!(), - | ^^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: aborting due to 4 previous errors - diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index 0a86144b95d5b..823be65efe065 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,6 +1,3 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 #![feature(exclusive_range_pattern)] #![allow(clippy::match_same_arms)] #![warn(clippy::match_wild_err_arm)] diff --git a/tests/ui/match_wild_err_arm.edition2018.stderr b/tests/ui/match_wild_err_arm.stderr similarity index 86% rename from tests/ui/match_wild_err_arm.edition2018.stderr rename to tests/ui/match_wild_err_arm.stderr index 525533bf07bb0..b016d682698c8 100644 --- a/tests/ui/match_wild_err_arm.edition2018.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 + --> $DIR/match_wild_err_arm.rs:11:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"), = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 + --> $DIR/match_wild_err_arm.rs:17:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -16,7 +16,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 + --> $DIR/match_wild_err_arm.rs:23:9 | LL | Err(_) => { | ^^^^^^ @@ -24,7 +24,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 + --> $DIR/match_wild_err_arm.rs:31:9 | LL | Err(_e) => panic!(), | ^^^^^^^ diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467f..ae237395b95fc 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b9..3202e99e0be9e 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f8582..dd8a50dab9002 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:15:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:19:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:22:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:25:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:26:13 + --> $DIR/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:29:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:30:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:34:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:35:13 + --> $DIR/mem_replace.rs:37:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:38:13 + --> $DIR/mem_replace.rs:40:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:41:13 + --> $DIR/mem_replace.rs:43:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:44:13 + --> $DIR/mem_replace.rs:46:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:47:13 + --> $DIR/mem_replace.rs:49:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:50:13 + --> $DIR/mem_replace.rs:52:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:53:13 + --> $DIR/mem_replace.rs:55:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:58:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:61:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:64:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:94:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1d..cd148063bf065 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,29 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +fn just_under_msrv() { + #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); -} - -fn option_as_ref_deref() { - let mut opt = Some(String::from("123")); - - let _ = opt.as_ref().map(String::as_str); - let _ = opt.as_ref().map(|x| x.as_str()); - let _ = opt.as_mut().map(String::as_mut_str); - let _ = opt.as_mut().map(|x| x.as_mut_str()); -} - -fn match_like_matches() { - let _y = match Some(5) { - Some(0) => true, - _ => false, - }; -} - -fn match_same_arms() { - match (1, 2, 3) { - (1, .., 3) => 42, - (.., 3) => 42, //~ ERROR match arms have same body - _ => 0, - }; -} - -fn match_same_arms2() { - let _ = match Some(42) { - Some(_) => 24, - None => 24, //~ ERROR match arms have same body - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -pub fn redundant_fieldnames() { - let start = 0; - let _ = RangeFrom { start: start }; -} - -pub fn redundant_static_lifetime() { - const VAR_ONE: &'static str = "Test constant #1"; -} - -pub fn checked_conversion() { - let value: i64 = 42; - let _ = value <= (u32::max_value() as i64) && value >= 0; - let _ = value <= (u32::MAX as i64) && value >= 0; -} - -pub struct FromOverInto(String); - -impl Into for String { - fn into(self) -> FromOverInto { - FromOverInto(self) - } -} - -pub fn filter_map_next() { - let a = ["1", "lol", "3", "NaN", "5"]; - - #[rustfmt::skip] - let _: Option = vec![1, 2, 3, 4, 5, 6] - .into_iter() - .filter_map(|x| { - if x == 2 { - Some(x * 2) - } else { - None - } - }) - .next(); -} - -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] -pub fn manual_range_contains() { - let x = 5; - x >= 8 && x < 12; -} - -pub fn use_self() { - struct Foo; - - impl Foo { - fn new() -> Foo { - Foo {} - } - fn test() -> Foo { - Foo::new() - } - } -} - -fn replace_with_default() { - let mut s = String::from("foo"); - let _ = std::mem::replace(&mut s, String::default()); -} - -fn map_unwrap_or() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt - .map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); -} - -// Could be const -fn missing_const_for_fn() -> i32 { - 1 -} - -fn unnest_or_patterns() { - struct TS(u8, u8); - if let TS(0, x) | TS(1, x) = TS(0, 0) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -fn main() { - filter_map_next(); - checked_conversion(); - redundant_fieldnames(); - redundant_static_lifetime(); - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - manual_range_contains(); - use_self(); - replace_with_default(); - map_unwrap_or(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); +fn meets_msrv() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } -mod just_under_msrv { - #![feature(custom_inner_attributes)] +fn just_above_msrv() { #![clippy::msrv = "1.44.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -mod meets_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.45.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } + let log2_10 = 3.321928094887362; } -mod just_above_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.46.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } +fn no_patch_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } +fn no_patch_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a8318..68aa58748190b 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,27 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a742..02892f329af67 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b9..93370a0fa9c91 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a8..0000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.40"] -#![clippy::msrv = "=1.35.0"] -#![clippy::msrv = "1.10.1"] - -mod foo { - #![clippy::msrv = "1"] - #![clippy::msrv = "1.0.0"] -} - -fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr deleted file mode 100644 index e3ff6605cde87..0000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 - | -LL | #![clippy::msrv = "=1.35.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 - | -LL | #![clippy::msrv = "1.10.1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 - | -LL | #![clippy::msrv = "1.0.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 - | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512b..0000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(clippy::redundant_clone)] -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0"] - -fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -fn main() { - manual_strip_msrv() -} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs deleted file mode 100644 index 551948bd72ef1..0000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(custom_inner_attributes)] - -#[clippy::msrv = "invalid.version"] -fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr deleted file mode 100644 index 579ee7a87d23c..0000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `msrv` cannot be an outer attribute - --> $DIR/min_rust_version_outer_attr.rs:3:1 - | -LL | #[clippy::msrv = "invalid.version"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - 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 88f6935d224ae..b85e88784918d 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -77,5 +77,17 @@ mod const_fn_stabilized_before_msrv { } } +fn msrv_1_45() -> i32 { + #![clippy::msrv = "1.45"] + + 45 +} + +fn msrv_1_46() -> i32 { + #![clippy::msrv = "1.46"] + + 46 +} + // Should not be const fn main() {} 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 3eb52b6827475..f8e221c82f1a5 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -81,5 +81,15 @@ LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:86:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | #![clippy::msrv = "1.46"] +LL | | +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs new file mode 100644 index 0000000000000..8df885919a3e6 --- /dev/null +++ b/tests/ui/missing_trait_methods.rs @@ -0,0 +1,50 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::missing_trait_methods)] + +trait A { + fn provided() {} +} + +trait B { + fn required(); + + fn a(_: usize) -> usize { + 1 + } + + fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + a.as_ref() + } +} + +struct Partial; + +impl A for Partial {} + +impl B for Partial { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } +} + +struct Complete; + +impl A for Complete { + fn provided() {} +} + +impl B for Complete { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } + + fn b>(a: &T) -> &[u8] { + a.as_ref() + } +} + +fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr new file mode 100644 index 0000000000000..0c5205e196572 --- /dev/null +++ b/tests/ui/missing_trait_methods.stderr @@ -0,0 +1,27 @@ +error: missing trait method provided by default: `provided` + --> $DIR/missing_trait_methods.rs:22:1 + | +LL | impl A for Partial {} + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:5:5 + | +LL | fn provided() {} + | ^^^^^^^^^^^^^ + = note: `-D clippy::missing-trait-methods` implied by `-D warnings` + +error: missing trait method provided by default: `b` + --> $DIR/missing_trait_methods.rs:24:1 + | +LL | impl B for Partial { + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:15:5 + | +LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index aa2687159ef47..340e89d2db1d2 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints([[""]]); multiple_constraints_normalizes_to_same(X, X); let _ = Some("").unwrap_or(""); + let _ = std::fs::write("x", "".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,8 +281,9 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { - takes_iter(&mut x) + takes_iter(x) } } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index d41251e8f6aac..c93711ac8e284 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints(&[[""]]); multiple_constraints_normalizes_to_same(&X, X); let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,6 +281,7 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { takes_iter(&mut x) } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(&x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(&x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 5af68706d4ba5..8b593268bec21 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:11:15 + --> $DIR/needless_borrow.rs:15:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,172 +7,208 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:15:13 + --> $DIR/needless_borrow.rs:19:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:13 + --> $DIR/needless_borrow.rs:31:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:29:15 + --> $DIR/needless_borrow.rs:33:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:35:27 + --> $DIR/needless_borrow.rs:39:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:46:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:47:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:44:15 + --> $DIR/needless_borrow.rs:48:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:45:15 + --> $DIR/needless_borrow.rs:49:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:48:11 + --> $DIR/needless_borrow.rs:52:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:13 + --> $DIR/needless_borrow.rs:59:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:13 + --> $DIR/needless_borrow.rs:60:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:57:23 + --> $DIR/needless_borrow.rs:61:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:58:23 + --> $DIR/needless_borrow.rs:62:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:67:14 + --> $DIR/needless_borrow.rs:71:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:73:14 + --> $DIR/needless_borrow.rs:77:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:85:13 + --> $DIR/needless_borrow.rs:89:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:87:22 + --> $DIR/needless_borrow.rs:91:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:97:5 + --> $DIR/needless_borrow.rs:101:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:106:5 + --> $DIR/needless_borrow.rs:110:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:131:51 + --> $DIR/needless_borrow.rs:135:51 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:132:44 + --> $DIR/needless_borrow.rs:136:44 | LL | let _ = std::path::Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:133:23 + --> $DIR/needless_borrow.rs:137:23 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:134:26 + --> $DIR/needless_borrow.rs:138:26 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:45 + --> $DIR/needless_borrow.rs:139:45 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:136:32 + --> $DIR/needless_borrow.rs:140:32 | LL | let _ = Some("").unwrap_or(&""); | ^^^ help: change this to: `""` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:141:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:187:13 + --> $DIR/needless_borrow.rs:192:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:196:13 + --> $DIR/needless_borrow.rs:201:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:298:55 + --> $DIR/needless_borrow.rs:286:20 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:304:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: aborting due to 29 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:344:37 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:345:37 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:364:15 + | +LL | debug(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:374:15 + | +LL | use_x(&x); + | ^^ help: change this to: `x` + +error: aborting due to 35 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c2..bc376d0d7fb39 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +43,17 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d3..ba3a2eedc225c 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +46,17 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f2823247528..7de8b3b6ba435 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:13:13 + --> $DIR/option_as_ref_deref.rs:14:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:16:13 + --> $DIR/option_as_ref_deref.rs:17:13 | LL | let _ = opt.clone() | _____________^ @@ -17,94 +17,100 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:22:13 + --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:24:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:34:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:36:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:42:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:46:13 + --> $DIR/option_as_ref_deref.rs:47:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` -error: aborting due to 17 previous errors +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:61:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 896430780ea80..23b1aa8bebd53 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2473163d4fd2f..039998f22dd71 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs new file mode 100644 index 0000000000000..668545da84419 --- /dev/null +++ b/tests/ui/partial_pub_fields.rs @@ -0,0 +1,40 @@ +#![allow(unused)] +#![warn(clippy::partial_pub_fields)] + +fn main() { + use std::collections::HashMap; + + #[derive(Default)] + pub struct FileSet { + files: HashMap, + pub paths: HashMap, + } + + pub struct Color { + pub r: u8, + pub g: u8, + b: u8, + } + + pub struct Point(i32, pub i32); + + pub struct Visibility { + r#pub: bool, + pub pos: u32, + } + + // Don't lint on empty structs; + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + // Don't lint on structs with one field. + pub struct Single1(i32); + pub struct Single2(pub i32); + pub struct Single3 { + v1: i32, + } + pub struct Single4 { + pub v1: i32, + } +} diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr new file mode 100644 index 0000000000000..84cfc1a91940d --- /dev/null +++ b/tests/ui/partial_pub_fields.stderr @@ -0,0 +1,35 @@ +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:10:9 + | +LL | pub paths: HashMap, + | ^^^ + | + = help: consider using private field here + = note: `-D clippy::partial-pub-fields` implied by `-D warnings` + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:16:9 + | +LL | b: u8, + | ^ + | + = help: consider using public field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:19:27 + | +LL | pub struct Point(i32, pub i32); + | ^^^ + | + = help: consider using private field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:23:9 + | +LL | pub pos: u32, + | ^^^ + | + = help: consider using private field here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index fd15001e540c6..5f54101ca15ad 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn do_vec(x: &Vec) { //Nothing here @@ -207,3 +207,31 @@ fn cow_conditional_to_mut(a: &mut Cow) { a.to_mut().push_str("foo"); } } + +// Issue #9542 +fn dyn_trait_ok(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for String {} + impl T for PathBuf {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} + +fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for [U] {} + impl T for String {} + impl T for str {} + impl T for PathBuf {} + impl T for Path {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d64b5f454a5aa..6b4de98ce88c6 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -162,5 +162,23 @@ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a sl LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 17 previous errors +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:17 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` + +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:35 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:51 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: aborting due to 20 previous errors diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e2..824f00cb99e85 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc13254..df925eeadfe5e 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a126..9689e665b05c5 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:14:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:19:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:24:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:29:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:46:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:50:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:52:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:57:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:57:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:58:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:79:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd469..34ab552cb1d8d 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c56828..a051b1f96f0fd 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df2241..8b82e062b93a6 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:37:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:58:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:59:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:60:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:60:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:62:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:86:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6ed..42110dbe81e84 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c93..bc5200bc8625b 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069a..735113460d28c 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:8:17 + --> $DIR/redundant_static_lifetimes.rs:9:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,94 +7,100 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:12:21 + --> $DIR/redundant_static_lifetimes.rs:13:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:32 + --> $DIR/redundant_static_lifetimes.rs:15:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:47 + --> $DIR/redundant_static_lifetimes.rs:15:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:16:17 + --> $DIR/redundant_static_lifetimes.rs:17:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:18:20 + --> $DIR/redundant_static_lifetimes.rs:19:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:20:19 + --> $DIR/redundant_static_lifetimes.rs:21:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:22:19 + --> $DIR/redundant_static_lifetimes.rs:23:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:24:19 + --> $DIR/redundant_static_lifetimes.rs:25:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:26:25 + --> $DIR/redundant_static_lifetimes.rs:27:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:30:29 + --> $DIR/redundant_static_lifetimes.rs:31:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:32:25 + --> $DIR/redundant_static_lifetimes.rs:33:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:34:28 + --> $DIR/redundant_static_lifetimes.rs:35:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:36:27 + --> $DIR/redundant_static_lifetimes.rs:37:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:38:27 + --> $DIR/redundant_static_lifetimes.rs:39:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:40:27 + --> $DIR/redundant_static_lifetimes.rs:41:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:68:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 17 previous errors diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d716..e487799e15220 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 3ca7a40190253..106274479751d 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {local_i32}"); + } + if local_i32 > 0 { + panic!("p2 {local_i32}"); + } + if local_i32 > 0 { + panic!("p3 {local_i32}"); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 924191f4324cf..8e495ebd083a5 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {}", local_i32); + } + if local_i32 > 0 { + panic!("p2 {0}", local_i32); + } + if local_i32 > 0 { + panic!("p3 {local_i32}", local_i32 = local_i32); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index d1a7749263429..2ce3b7fa960c6 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -828,7 +828,43 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:168:5 + --> $DIR/uninlined_format_args.rs:155:9 + | +LL | panic!("p1 {}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", local_i32); +LL + panic!("p1 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:158:9 + | +LL | panic!("p2 {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", local_i32); +LL + panic!("p2 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:161:9 + | +LL | panic!("p3 {local_i32}", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {local_i32}", local_i32 = local_i32); +LL + panic!("p3 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:181:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -839,5 +875,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 70 previous errors +error: aborting due to 73 previous errors diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed new file mode 100644 index 0000000000000..96cc0877960ec --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr new file mode 100644 index 0000000000000..2c8061259229a --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -0,0 +1,15 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: aborting due to previous error + diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed new file mode 100644 index 0000000000000..faf8ca4d3a797 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {var}"); + } + if var > 0 { + panic!("p2 {var}"); + } + if var > 0 { + panic!("p3 {var}"); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr new file mode 100644 index 0000000000000..0f09c45f41324 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -0,0 +1,51 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:14:9 + | +LL | panic!("p1 {}", var); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", var); +LL + panic!("p1 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:17:9 + | +LL | panic!("p2 {0}", var); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", var); +LL + panic!("p2 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:20:9 + | +LL | panic!("p3 {var}", var = var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {var}", var = var); +LL + panic!("p3 {var}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs new file mode 100644 index 0000000000000..6421c5bbed2f5 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.rs @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{}'", var); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263c..ec8c6abfab91e 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69ac..5213cdc269bd4 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index f97583aa22f9a..fe09aad06bc84 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa5394a565790..3de6d0903c0f1 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b2..9786c7b12128b 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e0..f57322396d4ac 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8fc..fbc12fff0b0e7 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:46:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed new file mode 100644 index 0000000000000..2930722b42d9d --- /dev/null +++ b/tests/ui/unused_format_specs.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{}", 1.0); + println!("{f} {f:?}"); + + println!("{}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs new file mode 100644 index 0000000000000..ee192a000d4b5 --- /dev/null +++ b/tests/ui/unused_format_specs.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{:.}", 1.0); + println!("{f:.} {f:.?}"); + + println!("{:.}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr new file mode 100644 index 0000000000000..7231c17e74c19 --- /dev/null +++ b/tests/ui/unused_format_specs.stderr @@ -0,0 +1,54 @@ +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:8:17 + | +LL | println!("{:.}", 1.0); + | ^ + | + = note: a precision specifier is not required to format floats + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: remove the `.` + | +LL - println!("{:.}", 1.0); +LL + println!("{}", 1.0); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:18 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f} {f:.?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:24 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f:.} {f:?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:11:17 + | +LL | println!("{:.}", 1); + | ^ + | +help: remove the `.` + | +LL - println!("{:.}", 1); +LL + println!("{}", 1); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs_unfixable.rs new file mode 100644 index 0000000000000..78601a3483d34 --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.rs @@ -0,0 +1,30 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format_args!("")); + //prints `abcde`, not `abc` + println!("{:.3}", format_args!("abcde")); + + println!("{:5}.", format_args_from_macro!()); + + let args = format_args!(""); + println!("{args:5}"); +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr new file mode 100644 index 0000000000000..9f1890282e6ac --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -0,0 +1,69 @@ +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:12:15 + | +LL | println!("{:5}.", format_args!("")); + | ^^^^ + | + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: for the width to apply consider using `format!()` + | +LL | println!("{:5}.", format!("")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args!("")); +LL + println!("{}.", format_args!("")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:14:15 + | +LL | println!("{:.3}", format_args!("abcde")); + | ^^^^^ + | +help: for the precision to apply consider using `format!()` + | +LL | println!("{:.3}", format!("abcde")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:.3}", format_args!("abcde")); +LL + println!("{}", format_args!("abcde")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:16:15 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:16:17 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args_from_macro!()); +LL + println!("{}.", format_args_from_macro!()); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:19:15 + | +LL | println!("{args:5}"); + | ^^^^^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:19:21 + | +LL | println!("{args:5}"); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{args:5}"); +LL + println!("{args}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da179..3b54fe9d5ff3d 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92ed..bf87633cd2d8f 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bde..16fb0609242cb 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:22:21 + --> $DIR/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,244 +7,250 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:13 + --> $DIR/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:25:22 + --> $DIR/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:13 + --> $DIR/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:31:25 + --> $DIR/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:13 + --> $DIR/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:24 + --> $DIR/use_self.rs:98:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:55 + --> $DIR/use_self.rs:98:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:13 + --> $DIR/use_self.rs:113:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:147:29 + --> $DIR/use_self.rs:148:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:159:21 + --> $DIR/use_self.rs:160:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:13 + --> $DIR/use_self.rs:161:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:177:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:180:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:223:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:224:13 + --> $DIR/use_self.rs:225:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:243:13 + --> $DIR/use_self.rs:244:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:257:25 + --> $DIR/use_self.rs:258:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:259:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:16 + --> $DIR/use_self.rs:263:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:22 + --> $DIR/use_self.rs:263:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:285:29 + --> $DIR/use_self.rs:286:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:13 + --> $DIR/use_self.rs:287:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:458:13 + --> $DIR/use_self.rs:459:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:495:13 + --> $DIR/use_self.rs:496:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:532:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:534:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:539:20 + --> $DIR/use_self.rs:540:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:563:17 + --> $DIR/use_self.rs:564:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:565:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:566:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:571:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:574:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:589:17 + --> $DIR/use_self.rs:590:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:594:17 + --> $DIR/use_self.rs:595:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:601:17 + --> $DIR/use_self.rs:602:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:606:17 + --> $DIR/use_self.rs:607:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:648:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 9e07769a8e4fd..a6d8d0307ce53 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -48,7 +48,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = String::from_utf8( - std::process::Command::new(&rustc) + std::process::Command::new(rustc) .arg("--version") .output() .expect("failed to run `rustc --version`") diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index c5d602ea3035f..e46ad2c6e0eec 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -442,6 +442,12 @@

Clippy Lints

All +
  • + +
  • ");
    +    let current_href = &context
    +        .href_from_span(clean::Span::new(file_span), false)
    +        .expect("only local crates should have sources emitted");
         match source_context {
             SourceContext::Standalone => {
                 extra = None;
                 for line in 1..=lines {
    -                writeln!(line_numbers, "{0}", line)
    +                writeln!(line_numbers, "{line}")
                 }
             }
    -        SourceContext::Embedded { offset, needs_expansion } => {
    +        SourceContext::Embedded { url, offset, needs_expansion } => {
                 extra =
                     if needs_expansion { Some(r#""#) } else { None };
    -            for line in 1..=lines {
    -                writeln!(line_numbers, "{0}", line + offset)
    +            for line_number in 1..=lines {
    +                let line = line_number + offset;
    +                writeln!(line_numbers, "{line}")
                 }
             }
         }
         line_numbers.write_str("