From ce4265663ded513b905f2371ba85cc119bae07e5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 10 Oct 2025 00:21:33 +0200 Subject: [PATCH 01/56] make the suggestions verbose they are much readable this way imo --- .../src/matches/match_like_matches.rs | 46 +++--- tests/ui/match_like_matches_macro.stderr | 135 ++++++++++++++++-- 2 files changed, 150 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index b5f631e8fea3..63bef58409ea 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,7 +1,7 @@ //! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` use super::REDUNDANT_PATTERN_MATCHING; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; @@ -43,18 +43,23 @@ pub(crate) fn check_if_let<'tcx>( { ex_new = ex_inner; } - span_lint_and_sugg( + span_lint_and_then( cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - "if let .. else expression looks like `matches!` macro", - "try", - format!( - "{}matches!({}, {pat})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, + "`if let .. else` expression looks like `matches!` macro", + |diag| { + diag.span_suggestion_verbose( + expr.span, + "use `matches!` directly", + format!( + "{}matches!({}, {pat})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + }, ); } } @@ -169,18 +174,23 @@ pub(super) fn check_match<'tcx>( { ex_new = ex_inner; } - span_lint_and_sugg( + span_lint_and_then( cx, MATCH_LIKE_MATCHES_MACRO, e.span, "match expression looks like `matches!` macro", - "try", - format!( - "{}matches!({}, {pat_and_guard})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, + |diag| { + diag.span_suggestion_verbose( + e.span, + "use `matches!` directly", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + }, ); true } else { diff --git a/tests/ui/match_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr index ae277ce4dca6..8ea13e04422e 100644 --- a/tests/ui/match_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -6,10 +6,18 @@ LL | let _y = match x { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `matches!(x, Some(0))` + | |_____^ | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` +help: use `matches!` directly + | +LL - let _y = match x { +LL - Some(0) => true, +LL - _ => false, +LL - }; +LL + let _y = matches!(x, Some(0)); + | error: redundant pattern matching, consider using `is_some()` --> tests/ui/match_like_matches_macro.rs:20:14 @@ -42,13 +50,28 @@ LL | let _zz = match x { LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` + | |_____^ + | +help: use `matches!` directly + | +LL - let _zz = match x { +LL - Some(r) if r == 0 => false, +LL - _ => true, +LL - }; +LL + let _zz = !matches!(x, Some(r) if r == 0); + | -error: if let .. else expression looks like `matches!` macro +error: `if let .. else` expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!` directly + | +LL - let _zzz = if let Some(5) = x { true } else { false }; +LL + let _zzz = matches!(x, Some(5)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:66:20 @@ -59,7 +82,17 @@ LL | | E::A(_) => true, LL | | E::B(_) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::A(_) => true, +LL - E::B(_) => true, +LL - _ => false, +LL - }; +LL + let _ans = matches!(x, E::A(_) | E::B(_)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:77:20 @@ -71,7 +104,19 @@ LL | | true ... | LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::A(_) => { +LL - true +LL - } +LL - E::B(_) => true, +LL - _ => false, +LL - }; +LL + let _ans = matches!(x, E::A(_) | E::B(_)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:88:20 @@ -82,7 +127,17 @@ LL | | E::B(_) => false, LL | | E::C => false, LL | | _ => true, LL | | }; - | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::B(_) => false, +LL - E::C => false, +LL - _ => true, +LL - }; +LL + let _ans = !matches!(x, E::B(_) | E::C); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:149:18 @@ -92,7 +147,16 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(z, Some(3))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _z = match &z { +LL - Some(3) => true, +LL - _ => false, +LL - }; +LL + let _z = matches!(z, Some(3)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:159:18 @@ -102,7 +166,16 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&z, Some(3))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _z = match &z { +LL - Some(3) => true, +LL - _ => false, +LL - }; +LL + let _z = matches!(&z, Some(3)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:177:21 @@ -112,7 +185,16 @@ LL | let _ = match &z { LL | | AnEnum::X => true, LL | | _ => false, LL | | }; - | |_____________^ help: try: `matches!(&z, AnEnum::X)` + | |_____________^ + | +help: use `matches!` directly + | +LL - let _ = match &z { +LL - AnEnum::X => true, +LL - _ => false, +LL - }; +LL + let _ = matches!(&z, AnEnum::X); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:192:20 @@ -122,7 +204,16 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&val, &Some(ref _a))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _res = match &val { +LL - &Some(ref _a) => true, +LL - _ => false, +LL - }; +LL + let _res = matches!(&val, &Some(ref _a)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:205:20 @@ -132,7 +223,16 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&val, &Some(ref _a))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _res = match &val { +LL - &Some(ref _a) => true, +LL - _ => false, +LL - }; +LL + let _res = matches!(&val, &Some(ref _a)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:264:14 @@ -142,7 +242,16 @@ LL | let _y = match Some(5) { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `matches!(Some(5), Some(0))` + | |_____^ + | +help: use `matches!` directly + | +LL - let _y = match Some(5) { +LL - Some(0) => true, +LL - _ => false, +LL - }; +LL + let _y = matches!(Some(5), Some(0)); + | error: aborting due to 14 previous errors From 7b83f421075bb9b8e68fb26570d18554a926526e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 10 Oct 2025 00:01:51 +0200 Subject: [PATCH 02/56] fix(match_like_matches_macro): don't create `matches!` with if-let guards --- .../src/matches/match_like_matches.rs | 6 ++- tests/ui/match_like_matches_macro.fixed | 7 +++ tests/ui/match_like_matches_macro.rs | 10 ++++ tests/ui/match_like_matches_macro.stderr | 21 +++++++- .../match_like_matches_macro_if_let_guard.rs | 51 +++++++++++++++++++ 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/ui/match_like_matches_macro_if_let_guard.rs diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 63bef58409ea..89411115f730 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -2,6 +2,7 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::has_let_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; @@ -92,7 +93,10 @@ pub(super) fn check_match<'tcx>( // ```rs // matches!(e, Either::Left $(if $guard)|+) // ``` - middle_arms.is_empty() + // + // But if the guard _is_ present, it may not be an `if-let` guard, as `matches!` doesn't + // support these (currently?) + (middle_arms.is_empty() && first_arm.guard.is_none_or(|g| !has_let_expr(g))) // - (added in #6216) There are middle arms // diff --git a/tests/ui/match_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed index a1c95e8a94f1..dad59c1ce6e4 100644 --- a/tests/ui/match_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -223,3 +223,10 @@ fn msrv_1_42() { let _y = matches!(Some(5), Some(0)); //~^^^^ match_like_matches_macro } + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + // Lint: no if-let _in the guard_ + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() })); + //~^^^^ match_like_matches_macro +} diff --git a/tests/ui/match_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs index eb419ba5bf8d..94bc6433e5cb 100644 --- a/tests/ui/match_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -267,3 +267,13 @@ fn msrv_1_42() { }; //~^^^^ match_like_matches_macro } + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + // Lint: no if-let _in the guard_ + let _ = match opt { + Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, + _ => false, + }; + //~^^^^ match_like_matches_macro +} diff --git a/tests/ui/match_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr index 8ea13e04422e..a8e352461dbb 100644 --- a/tests/ui/match_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -253,5 +253,24 @@ LL - }; LL + let _y = matches!(Some(5), Some(0)); | -error: aborting due to 14 previous errors +error: match expression looks like `matches!` macro + --> tests/ui/match_like_matches_macro.rs:274:13 + | +LL | let _ = match opt { + | _____________^ +LL | | Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, +LL | | _ => false, +LL | | }; + | |_____^ + | +help: use `matches!` directly + | +LL - let _ = match opt { +LL - Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, +LL - _ => false, +LL - }; +LL + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() })); + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_like_matches_macro_if_let_guard.rs b/tests/ui/match_like_matches_macro_if_let_guard.rs new file mode 100644 index 000000000000..b596d36072e5 --- /dev/null +++ b/tests/ui/match_like_matches_macro_if_let_guard.rs @@ -0,0 +1,51 @@ +//@check-pass +#![warn(clippy::match_like_matches_macro)] +#![feature(if_let_guard)] + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + let _ = match opt { + Some(first) + if let Some(second) = first + && let Some(third) = second + && third == value => + { + true + }, + _ => false, + }; + + // if-let is the second if + let _ = match opt { + Some(first) + if first.is_some() + && let Some(second) = first => + { + true + }, + _ => false, + }; + + // if-let is the third if + let _ = match opt { + Some(first) + if first.is_some() + && first.is_none() + && let Some(second) = first => + { + true + }, + _ => false, + }; + + // don't get confused by `or`s + let _ = match opt { + Some(first) + if (first.is_some() || first.is_none()) + && let Some(second) = first => + { + true + }, + _ => false, + }; +} From 3833ac3be02c3e9853570660224d14dced9250f2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 16:38:57 +0200 Subject: [PATCH 03/56] misc: use `Symbol`s instead of `&str`s --- .../methods/manual_saturating_arithmetic.rs | 67 ++++++++++++++----- clippy_lints/src/methods/mod.rs | 9 +-- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 2196ce92b0ab..21526c83c318 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -8,6 +8,7 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; +use rustc_span::Symbol; pub fn check( cx: &LateContext<'_>, @@ -15,7 +16,7 @@ pub fn check( arith_lhs: &hir::Expr<'_>, arith_rhs: &hir::Expr<'_>, unwrap_arg: &hir::Expr<'_>, - arith: &str, + arith: Symbol, ) { let ty = cx.typeck_results().expr_ty(arith_lhs); if !ty.is_integral() { @@ -26,35 +27,43 @@ pub fn check( return; }; - if ty.is_signed() { + let Some(checked_arith) = CheckedArith::new(arith) else { + return; + }; + + { use self::MinMax::{Max, Min}; use self::Sign::{Neg, Pos}; + use CheckedArith::{Add, Mul, Sub}; - let Some(sign) = lit_sign(arith_rhs) else { - return; - }; + if ty.is_signed() { + let Some(sign) = lit_sign(arith_rhs) else { + return; + }; - match (arith, sign, mm) { - ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (), - // "mul" is omitted because lhs can be negative. - _ => return, - } - } else { - match (mm, arith) { - (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (), - _ => return, + match (&checked_arith, sign, mm) { + (Add, Pos, Max) | (Add, Neg, Min) | (Sub, Neg, Max) | (Sub, Pos, Min) => (), + // "mul" is omitted because lhs can be negative. + _ => return, + } + } else { + match (mm, &checked_arith) { + (Max, Add | Mul) | (Min, Sub) => (), + _ => return, + } } } let mut applicability = Applicability::MachineApplicable; + let saturating_arith = checked_arith.as_saturating(); span_lint_and_sugg( cx, super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - format!("consider using `saturating_{arith}`"), + format!("consider using `{saturating_arith}`"), format!( - "{}.saturating_{arith}({})", + "{}.{saturating_arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), ), @@ -62,6 +71,32 @@ pub fn check( ); } +enum CheckedArith { + Add, + Sub, + Mul, +} + +impl CheckedArith { + fn new(sym: Symbol) -> Option { + let res = match sym { + sym::checked_add => Self::Add, + sym::checked_sub => Self::Sub, + sym::checked_mul => Self::Mul, + _ => return None, + }; + Some(res) + } + + fn as_saturating(&self) -> &'static str { + match self { + Self::Add => "saturating_add", + Self::Sub => "saturating_sub", + Self::Mul => "saturating_mul", + } + } +} + #[derive(PartialEq, Eq)] enum MinMax { Min, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44..5702d08aa6d3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5507,14 +5507,7 @@ impl Methods { (sym::unwrap_or, [u_arg]) => { match method_call(recv) { Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => { - manual_saturating_arithmetic::check( - cx, - expr, - lhs, - rhs, - u_arg, - &arith.as_str()[const { "checked_".len() }..], - ); + manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, arith); }, Some((sym::map, m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); From 95834c9f6008a2f6fda31db7e990bd816cc297b3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 17:15:13 +0200 Subject: [PATCH 04/56] recognize `x.checked_sub(y).unwrap_or_default()` as well --- .../methods/manual_saturating_arithmetic.rs | 90 +++++++++++++------ clippy_lints/src/methods/mod.rs | 5 +- tests/ui/manual_saturating_arithmetic.fixed | 10 +++ tests/ui/manual_saturating_arithmetic.rs | 10 +++ tests/ui/manual_saturating_arithmetic.stderr | 8 +- 5 files changed, 93 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 21526c83c318..a3dd967bd77a 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -4,18 +4,19 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_hir::{self as hir, Expr}; use rustc_lint::LateContext; +use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; use rustc_span::Symbol; -pub fn check( +pub fn check_unwrap_or( cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - arith_lhs: &hir::Expr<'_>, - arith_rhs: &hir::Expr<'_>, - unwrap_arg: &hir::Expr<'_>, + expr: &Expr<'_>, + arith_lhs: &Expr<'_>, + arith_rhs: &Expr<'_>, + unwrap_arg: &Expr<'_>, arith: Symbol, ) { let ty = cx.typeck_results().expr_ty(arith_lhs); @@ -31,26 +32,58 @@ pub fn check( return; }; - { - use self::MinMax::{Max, Min}; - use self::Sign::{Neg, Pos}; - use CheckedArith::{Add, Mul, Sub}; + check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith); +} - if ty.is_signed() { - let Some(sign) = lit_sign(arith_rhs) else { - return; - }; +pub(super) fn check_sub_unwrap_or_default( + cx: &LateContext<'_>, + expr: &Expr<'_>, + arith_lhs: &Expr<'_>, + arith_rhs: &Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(arith_lhs); + if !ty.is_integral() { + return; + } - match (&checked_arith, sign, mm) { - (Add, Pos, Max) | (Add, Neg, Min) | (Sub, Neg, Max) | (Sub, Pos, Min) => (), - // "mul" is omitted because lhs can be negative. - _ => return, - } - } else { - match (mm, &checked_arith) { - (Max, Add | Mul) | (Min, Sub) => (), - _ => return, - } + let mm = if ty.is_signed() { + return; // iN::default() is 0, which is neither MIN nor MAX + } else { + MinMax::Min // uN::default() is 0, which is also the MIN + }; + + let checked_arith = CheckedArith::Sub; + + check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith); +} + +fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + arith_lhs: &Expr<'_>, + arith_rhs: &Expr<'_>, + ty: Ty<'_>, + mm: MinMax, + checked_arith: CheckedArith, +) { + use self::MinMax::{Max, Min}; + use self::Sign::{Neg, Pos}; + use CheckedArith::{Add, Mul, Sub}; + + if ty.is_signed() { + let Some(sign) = lit_sign(arith_rhs) else { + return; + }; + + match (checked_arith, sign, mm) { + (Add, Pos, Max) | (Add, Neg, Min) | (Sub, Neg, Max) | (Sub, Pos, Min) => (), + // "mul" is omitted because lhs can be negative. + _ => return, + } + } else { + match (mm, checked_arith) { + (Max, Add | Mul) | (Min, Sub) => (), + _ => return, } } @@ -71,6 +104,7 @@ pub fn check( ); } +#[derive(Clone, Copy)] enum CheckedArith { Add, Sub, @@ -88,7 +122,7 @@ impl CheckedArith { Some(res) } - fn as_saturating(&self) -> &'static str { + fn as_saturating(self) -> &'static str { match self { Self::Add => "saturating_add", Self::Sub => "saturating_sub", @@ -103,7 +137,7 @@ enum MinMax { Max, } -fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { +fn is_min_or_max(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { // `T::max_value()` `T::min_value()` inherent methods if let hir::ExprKind::Call(func, []) = &expr.kind && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind @@ -141,7 +175,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { (0, if bits == 128 { !0 } else { (1 << bits) - 1 }) }; - let check_lit = |expr: &hir::Expr<'_>, check_min: bool| { + let check_lit = |expr: &Expr<'_>, check_min: bool| { if let hir::ExprKind::Lit(lit) = &expr.kind && let ast::LitKind::Int(value, _) = lit.node { @@ -176,7 +210,7 @@ enum Sign { Neg, } -fn lit_sign(expr: &hir::Expr<'_>) -> Option { +fn lit_sign(expr: &Expr<'_>) -> Option { if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind { if let hir::ExprKind::Lit(..) = &inner.kind { return Some(Sign::Neg); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5702d08aa6d3..d8181dcf19f0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5507,7 +5507,7 @@ impl Methods { (sym::unwrap_or, [u_arg]) => { match method_call(recv) { Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, arith); + manual_saturating_arithmetic::check_unwrap_or(cx, expr, lhs, rhs, u_arg, arith); }, Some((sym::map, m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); @@ -5528,6 +5528,9 @@ impl Methods { }, (sym::unwrap_or_default, []) => { match method_call(recv) { + Some((sym::checked_sub, lhs, [rhs], _, _)) => { + manual_saturating_arithmetic::check_sub_unwrap_or_default(cx, expr, lhs, rhs); + }, Some((sym::map, m_recv, [arg], span, _)) => { manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); }, diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed index 304be05f6c4c..8dd142b2c79a 100644 --- a/tests/ui/manual_saturating_arithmetic.fixed +++ b/tests/ui/manual_saturating_arithmetic.fixed @@ -58,3 +58,13 @@ fn main() { let _ = 1i8.checked_sub(1).unwrap_or(127); // ok let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok } + +fn issue15655() { + let _ = 5u32.saturating_sub(1u32); //~ manual_saturating_arithmetic + let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok + let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok + + let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok + let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok + let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok +} diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs index c2b570e974ac..9cc8bc42410d 100644 --- a/tests/ui/manual_saturating_arithmetic.rs +++ b/tests/ui/manual_saturating_arithmetic.rs @@ -73,3 +73,13 @@ fn main() { let _ = 1i8.checked_sub(1).unwrap_or(127); // ok let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok } + +fn issue15655() { + let _ = 5u32.checked_sub(1u32).unwrap_or_default(); //~ manual_saturating_arithmetic + let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok + let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok + + let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok + let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok + let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok +} diff --git a/tests/ui/manual_saturating_arithmetic.stderr b/tests/ui/manual_saturating_arithmetic.stderr index 2f006a3ae170..aec0f0a16419 100644 --- a/tests/ui/manual_saturating_arithmetic.stderr +++ b/tests/ui/manual_saturating_arithmetic.stderr @@ -165,5 +165,11 @@ LL | | .checked_sub(-1) LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); | |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)` -error: aborting due to 24 previous errors +error: manual saturating arithmetic + --> tests/ui/manual_saturating_arithmetic.rs:78:13 + | +LL | let _ = 5u32.checked_sub(1u32).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `5u32.saturating_sub(1u32)` + +error: aborting due to 25 previous errors From 96ac0996a0d41e80536343bc861b2cda6d54d8d5 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 2 Sep 2025 00:40:54 +0800 Subject: [PATCH 05/56] fix: `nonstandard_macro_braces` FN on macros with empty args --- clippy_lints/src/nonstandard_macro_braces.rs | 38 +++++++++------- .../conf_nonstandard_macro_braces.fixed | 14 ++++++ .../conf_nonstandard_macro_braces.rs | 14 ++++++ .../conf_nonstandard_macro_braces.stderr | 44 ++++++++++++++----- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 3a8a4dd0c713..8970970f4b47 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -115,35 +115,41 @@ impl EarlyLintPass for MacroBraces { } fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBraces) -> Option { - let unnested_or_local = || { - !span.ctxt().outer_expn_data().call_site.from_expansion() + let unnested_or_local = |span: Span| { + !span.from_expansion() || span .macro_backtrace() .last() .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; - let callsite_span = span.ctxt().outer_expn_data().call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind + + let mut ctxt = span.ctxt(); + while !ctxt.is_root() { + let expn_data = ctxt.outer_expn_data(); + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = callsite_span.get_source_text(cx) + && let Some(snip) = expn_data.call_site.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) && let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next() && old_open_brace != braces.0 - && unnested_or_local() - && !mac_braces.done.contains(&callsite_span) - { - Some(MacroInfo { - callsite_span, - callsite_snippet: snip, - old_open_brace, - braces, - }) - } else { - None + && unnested_or_local(expn_data.call_site) + && !mac_braces.done.contains(&expn_data.call_site) + { + return Some(MacroInfo { + callsite_span: expn_data.call_site, + callsite_snippet: snip, + old_open_brace, + braces, + }); + } + + ctxt = expn_data.call_site.ctxt(); } + + None } fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) { diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed index 419e62f92f46..3683e826aa92 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -1,6 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::nonstandard_macro_braces)] +#![allow(clippy::println_empty_string)] extern crate proc_macro_derive; extern crate quote; @@ -75,3 +76,16 @@ fn issue9913() { [0]; // separate statement, not indexing into the result of println. //~^^ nonstandard_macro_braces } + +fn issue15594() { + println!(); + println!(""); + println!(); + //~^ nonstandard_macro_braces + println!(""); + //~^ nonstandard_macro_braces + println!(); + //~^ nonstandard_macro_braces + println!(""); + //~^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index b0bbced4ea3c..c1779dceff17 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -1,6 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::nonstandard_macro_braces)] +#![allow(clippy::println_empty_string)] extern crate proc_macro_derive; extern crate quote; @@ -75,3 +76,16 @@ fn issue9913() { [0]; // separate statement, not indexing into the result of println. //~^^ nonstandard_macro_braces } + +fn issue15594() { + println!(); + println!(""); + println![]; + //~^ nonstandard_macro_braces + println![""]; + //~^ nonstandard_macro_braces + println! {}; + //~^ nonstandard_macro_braces + println! {""}; + //~^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 87325f05c9bc..2488f7fa01e5 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -1,5 +1,5 @@ error: use of irregular braces for `vec!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:44:13 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = vec! {1, 2, 3}; | ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]` @@ -8,31 +8,31 @@ LL | let _ = vec! {1, 2, 3}; = help: to override `-D warnings` add `#[allow(clippy::nonstandard_macro_braces)]` error: use of irregular braces for `format!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:46:13 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")` error: use of irregular braces for `matches!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:48:13 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:49:13 | LL | let _ = matches!{{}, ()}; | ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())` error: use of irregular braces for `quote!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:50:13 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:51:13 | LL | let _ = quote!(let x = 1;); | ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}` error: use of irregular braces for `quote::quote!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:52:13 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:53:13 | LL | let _ = quote::quote!(match match match); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}` error: use of irregular braces for `vec!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:18:9 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:19:9 | LL | vec!{0, 0, 0} | ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]` @@ -43,22 +43,46 @@ LL | let _ = test!(); // trigger when macro def is inside our own crate = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:62:12 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:63:12 | LL | let _: type_pos!(usize) = vec![]; | ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]` error: use of irregular braces for `eprint!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:65:5 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:66:5 | LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` error: use of irregular braces for `println!` macro - --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5 + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:75:5 | LL | println! {"hello world"} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");` -error: aborting due to 9 previous errors +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:83:5 + | +LL | println![]; + | ^^^^^^^^^^ help: consider writing: `println!()` + +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:85:5 + | +LL | println![""]; + | ^^^^^^^^^^^^ help: consider writing: `println!("")` + +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:87:5 + | +LL | println! {}; + | ^^^^^^^^^^^ help: consider writing: `println!()` + +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:89:5 + | +LL | println! {""}; + | ^^^^^^^^^^^^^ help: consider writing: `println!("")` + +error: aborting due to 13 previous errors From f4496bf9b653faff16f271c09105ed620792b920 Mon Sep 17 00:00:00 2001 From: Sergio Giro Date: Sun, 16 Nov 2025 17:49:10 +0000 Subject: [PATCH 06/56] Fix suggestions in while_let_on_iterator for non-sized traits --- .../src/loops/while_let_on_iterator.rs | 29 +++++++++++++---- tests/ui/while_let_on_iterator.fixed | 31 +++++++++++++++++++ tests/ui/while_let_on_iterator.rs | 31 +++++++++++++++++++ tests/ui/while_let_on_iterator.stderr | 16 ++++++++-- 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 2545f81f1afa..c063e9263ba0 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,6 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; +use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -43,26 +44,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }; // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be - // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // passed by reference. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + let iterator_by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) || !iter_expr_struct.can_move || !iter_expr_struct.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr_struct, expr) { - ".by_ref()" + make_iterator_snippet(cx, iter_expr, iterator) } else { - "" + iterator.to_string() }; - let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); span_lint_and_sugg( cx, WHILE_LET_ON_ITERATOR, expr.span.with_hi(let_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("{loop_label}for {loop_var} in {iterator}{by_ref}"), + format!("{loop_label}for {loop_var} in {iterator_by_ref}"), applicability, ); } @@ -355,3 +356,19 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & .is_break() } } + +/// Constructs the transformed iterator expression for the suggestion. +/// Returns `iterator.by_ref()` unless the iterator type is a reference to an unsized type, +/// in which case it returns `&mut *iterator`. +fn make_iterator_snippet<'tcx>(cx: &LateContext<'tcx>, iter_expr: &Expr<'tcx>, iterator: impl Into) -> String { + let iterator = iterator.into(); + let ty = cx.typeck_results().expr_ty(iter_expr); + + if let ty::Ref(_, inner_ty, _) = ty.kind() + && !inner_ty.is_sized(cx.tcx, cx.typing_env()) + { + return format!("&mut *{iterator}"); + } + + format!("{iterator}.by_ref()") +} diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index f9ccefab5898..4e03e954108e 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -492,6 +492,37 @@ fn issue13123() { } } +fn issue16089() { + trait CertainTrait: Iterator { + fn iter_over_self(&mut self) { + let mut a = 0; + for r in &mut *self { + //~^ while_let_on_iterator + a = r; + } + self.use_after_iter() + } + + fn use_after_iter(&mut self) {} + } +} + +fn issue16089_sized_trait_not_reborrowed() { + trait CertainTrait: Iterator + Sized { + fn iter_over_self(&mut self) { + let mut a = 0; + // Check that the suggestion is just "self", since the trait is sized. + for r in self.by_ref() { + //~^ while_let_on_iterator + a = r; + } + self.use_after_iter() + } + + fn use_after_iter(&mut self) {} + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index f957f2e5a523..cc65fda6d18f 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -492,6 +492,37 @@ fn issue13123() { } } +fn issue16089() { + trait CertainTrait: Iterator { + fn iter_over_self(&mut self) { + let mut a = 0; + while let Some(r) = self.next() { + //~^ while_let_on_iterator + a = r; + } + self.use_after_iter() + } + + fn use_after_iter(&mut self) {} + } +} + +fn issue16089_sized_trait_not_reborrowed() { + trait CertainTrait: Iterator + Sized { + fn iter_over_self(&mut self) { + let mut a = 0; + // Check that the suggestion is just "self", since the trait is sized. + while let Some(r) = self.next() { + //~^ while_let_on_iterator + a = r; + } + self.use_after_iter() + } + + fn use_after_iter(&mut self) {} + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 50f20227b90f..21ebc22f699d 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -164,10 +164,22 @@ LL | 'label: while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it` error: this loop could be written as a `for` loop - --> tests/ui/while_let_on_iterator.rs:497:5 + --> tests/ui/while_let_on_iterator.rs:499:13 + | +LL | while let Some(r) = self.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in &mut *self` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:515:13 + | +LL | while let Some(r) = self.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in self.by_ref()` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:528:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 28 previous errors +error: aborting due to 30 previous errors From 6a14a523d286adb2d1d7265f20b6a8e2bd042466 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 20 Nov 2025 12:56:12 +0100 Subject: [PATCH 07/56] clean-up: pass things by value to avoid copying --- clippy_lints/src/missing_asserts_for_indexing.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 808adb7e71ce..9e9aa5ffc544 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -67,14 +67,14 @@ declare_clippy_lint! { } declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]); -fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &'static str, indexes: &[Span], f: F) +fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &'static str, indexes: Vec, f: F) where F: FnOnce(&mut Diag<'_, ()>), { span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| { f(diag); for span in indexes { - diag.span_note(*span, "slice indexed here"); + diag.span_note(span, "slice indexed here"); } diag.note("asserting the length before indexing will elide bounds checks"); }); @@ -354,8 +354,8 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un /// Inspects indexes and reports lints. /// /// Called at the end of this lint after all indexing and `assert!` expressions have been collected. -fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap>>) { - for bucket in map.values() { +fn report_indexes(cx: &LateContext<'_>, map: UnindexMap>>) { + for bucket in map.into_values() { for entry in bucket { let Some(full_span) = entry .index_spans() @@ -365,12 +365,12 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap continue; }; - match *entry { + match entry { IndexEntry::AssertWithIndex { highest_index, is_first_highest, asserted_len, - ref indexes, + indexes, comparison, assert_span, slice, @@ -433,7 +433,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap } }, IndexEntry::IndexWithoutAssert { - ref indexes, + indexes, highest_index, is_first_highest, slice, @@ -469,6 +469,6 @@ impl LateLintPass<'_> for MissingAssertsForIndexing { ControlFlow::::Continue(()) }); - report_indexes(cx, &map); + report_indexes(cx, map); } } From 9ced0f5892eaff77f7b707fbd41be92d38b42028 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 20 Nov 2025 12:58:16 +0100 Subject: [PATCH 08/56] use `note_once` This is a general advice, and so shouldn't be repeated --- clippy_lints/src/missing_asserts_for_indexing.rs | 2 +- tests/ui/missing_asserts_for_indexing.stderr | 14 -------------- .../missing_asserts_for_indexing_unfixable.stderr | 9 --------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 9e9aa5ffc544..ea65aa274dc4 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -76,7 +76,7 @@ where for span in indexes { diag.span_note(span, "slice indexed here"); } - diag.note("asserting the length before indexing will elide bounds checks"); + diag.note_once("asserting the length before indexing will elide bounds checks"); }); } diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr index b686eda7530a..d6eef15c0448 100644 --- a/tests/ui/missing_asserts_for_indexing.stderr +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -68,7 +68,6 @@ note: slice indexed here | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:42:5 @@ -103,7 +102,6 @@ note: slice indexed here | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:48:5 @@ -138,7 +136,6 @@ note: slice indexed here | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:66:13 @@ -161,7 +158,6 @@ note: slice indexed here | LL | let _ = v[1..4]; | ^^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:81:13 @@ -184,7 +180,6 @@ note: slice indexed here | LL | let _ = v[1..=4]; | ^^^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:97:13 @@ -205,7 +200,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[12]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:100:13 @@ -226,7 +220,6 @@ note: slice indexed here | LL | let _ = v2[5] + v2[15]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:106:13 @@ -247,7 +240,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[12]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:131:13 @@ -273,7 +265,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:136:13 @@ -299,7 +290,6 @@ note: slice indexed here | LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:158:13 @@ -325,7 +315,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:163:13 @@ -351,7 +340,6 @@ note: slice indexed here | LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:172:17 @@ -376,7 +364,6 @@ note: slice indexed here | LL | let _ = v[0] + v[1] + v[2]; | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:178:17 @@ -401,7 +388,6 @@ note: slice indexed here | LL | let _ = v[0] + v[1] + v[2]; | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: aborting due to 15 previous errors diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr index a17ad0232138..aec64857a699 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -54,7 +54,6 @@ note: slice indexed here | LL | let _ = v[1..4]; | ^^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 @@ -83,7 +82,6 @@ note: slice indexed here | LL | let c = v[2]; | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 @@ -102,7 +100,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[12]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 @@ -121,7 +118,6 @@ note: slice indexed here | LL | let _ = v2[5] + v2[15]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 @@ -140,7 +136,6 @@ note: slice indexed here | LL | let _ = v2[5] + v2[15]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 @@ -159,7 +154,6 @@ note: slice indexed here | LL | let _ = f.v[0] + f.v[1]; | ^^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 @@ -178,7 +172,6 @@ note: slice indexed here | LL | let _ = x[0] + x[1]; | ^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 @@ -197,7 +190,6 @@ note: slice indexed here | LL | let _ = v1[1] + v1[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 @@ -221,7 +213,6 @@ note: slice indexed here | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ - = note: asserting the length before indexing will elide bounds checks error: aborting due to 10 previous errors From 4b021f6b62c5210b4c0e37ae3eb3873081e92128 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 20 Nov 2025 13:30:54 +0100 Subject: [PATCH 09/56] point the suggestion at each individual span This makes the labels redundant. --- .../src/missing_asserts_for_indexing.rs | 30 +- tests/ui/missing_asserts_for_indexing.stderr | 292 ++---------------- ...sing_asserts_for_indexing_unfixable.stderr | 153 +-------- 3 files changed, 37 insertions(+), 438 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index ea65aa274dc4..a89a460cdfb3 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -67,15 +67,12 @@ declare_clippy_lint! { } declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]); -fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &'static str, indexes: Vec, f: F) +fn report_lint(cx: &LateContext<'_>, index_spans: Vec, msg: &'static str, f: F) where F: FnOnce(&mut Diag<'_, ()>), { - span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| { + span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, index_spans, msg, |diag| { f(diag); - for span in indexes { - diag.span_note(span, "slice indexed here"); - } diag.note_once("asserting the length before indexing will elide bounds checks"); }); } @@ -213,15 +210,6 @@ impl<'hir> IndexEntry<'hir> { | IndexEntry::IndexWithoutAssert { slice, .. } => slice, } } - - pub fn index_spans(&self) -> Option<&[Span]> { - match self { - IndexEntry::StrayAssert { .. } => None, - IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => { - Some(indexes) - }, - } - } } /// Extracts the upper index of a slice indexing expression. @@ -357,14 +345,6 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un fn report_indexes(cx: &LateContext<'_>, map: UnindexMap>>) { for bucket in map.into_values() { for entry in bucket { - let Some(full_span) = entry - .index_spans() - .and_then(|spans| spans.first().zip(spans.last())) - .map(|(low, &high)| low.to(high)) - else { - continue; - }; - match entry { IndexEntry::AssertWithIndex { highest_index, @@ -418,9 +398,8 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> if let Some(sugg) = sugg { report_lint( cx, - full_span, - "indexing into a slice multiple times with an `assert` that does not cover the highest index", indexes, + "indexing into a slice multiple times with an `assert` that does not cover the highest index", |diag| { diag.span_suggestion( assert_span, @@ -442,9 +421,8 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> // adding an `assert!` that covers the highest index report_lint( cx, - full_span, - "indexing into a slice multiple times without an `assert`", indexes, + "indexing into a slice multiple times without an `assert`", |diag| { diag.help(format!( "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`", diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr index d6eef15c0448..30a35a21e5d3 100644 --- a/tests/ui/missing_asserts_for_indexing.stderr +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -4,33 +4,8 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | assert!(v.len() < 5); | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:30:5 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:30:12 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:30:19 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:30:26 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:30:33 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ = note: asserting the length before indexing will elide bounds checks = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` @@ -41,33 +16,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | assert!(v.len() <= 5); | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:36:5 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:36:12 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:36:19 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:36:26 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:36:33 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ + | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:42:5 @@ -75,33 +24,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | assert!(v.len() > 3); | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:42:5 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:42:12 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:42:19 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:42:26 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:42:33 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ + | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:48:5 @@ -109,75 +32,27 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | assert!(v.len() >= 4); | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:48:5 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:48:12 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:48:19 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:48:26 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:48:33 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ + | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:66:13 | -LL | assert!(v.len() >= 3); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)` -LL | let _ = v[0]; - | _____________^ -... | -LL | | let _ = v[1..4]; - | |___________________^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:66:13 - | +LL | assert!(v.len() >= 3); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)` LL | let _ = v[0]; | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:69:13 - | +... LL | let _ = v[1..4]; | ^^^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:81:13 | -LL | assert!(v.len() >= 4); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` -LL | let _ = v[0]; - | _____________^ -... | -LL | | let _ = v[1..=4]; - | |____________________^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:81:13 - | +LL | assert!(v.len() >= 4); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | let _ = v[0]; | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:84:13 - | +... LL | let _ = v[1..=4]; | ^^^^^^^^ @@ -188,18 +63,7 @@ LL | assert!(v1.len() >= 12); | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` LL | assert!(v2.len() >= 15); LL | let _ = v1[0] + v1[12]; - | ^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:97:13 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:97:21 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^^ + | ^^^^^ ^^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:100:13 @@ -208,18 +72,7 @@ LL | assert!(v2.len() >= 15); | ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)` ... LL | let _ = v2[5] + v2[15]; - | ^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:100:13 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:100:21 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^^ + | ^^^^^ ^^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:106:13 @@ -228,18 +81,7 @@ LL | assert!(v1.len() >= 12); | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` LL | assert!(v2.len() > 15); LL | let _ = v1[0] + v1[12]; - | ^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:106:13 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:106:21 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^^ + | ^^^^^ ^^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:131:13 @@ -248,23 +90,7 @@ LL | assert!(v1.len() == 2); | ---------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` ... LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:131:13 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:131:21 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:131:29 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ + | ^^^^^ ^^^^^ ^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:136:13 @@ -273,23 +99,7 @@ LL | assert!(2 == v3.len()); | ---------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` ... LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:136:13 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:136:21 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:136:29 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ + | ^^^^^ ^^^^^ ^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:158:13 @@ -298,23 +108,7 @@ LL | assert_eq!(v1.len(), 2); | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)` ... LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:158:13 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:158:21 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:158:29 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ + | ^^^^^ ^^^^^ ^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:163:13 @@ -323,23 +117,7 @@ LL | assert_eq!(2, v3.len()); | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)` ... LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:163:13 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:163:21 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:163:29 - | -LL | let _ = v3[0] + v3[1] + v3[2]; - | ^^^^^ + | ^^^^^ ^^^^^ ^^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:172:17 @@ -347,23 +125,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | assert_eq!(v.len(), 2); | ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)` LL | let _ = v[0] + v[1] + v[2]; - | ^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:172:17 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:172:24 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:172:31 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ + | ^^^^ ^^^^ ^^^^ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:178:17 @@ -371,23 +133,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover LL | debug_assert_eq!(v.len(), 2); | ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)` LL | let _ = v[0] + v[1] + v[2]; - | ^^^^^^^^^^^^^^^^^^ - | -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:178:17 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:178:24 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing.rs:178:31 - | -LL | let _ = v[0] + v[1] + v[2]; - | ^^^^ + | ^^^^ ^^^^ ^^^^ error: aborting due to 15 previous errors diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr index aec64857a699..2929646494a4 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -2,34 +2,9 @@ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 | LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ | = help: consider asserting the length before indexing: `assert!(v.len() > 4);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:12 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:19 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:26 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:33 - | -LL | v[0] + v[1] + v[2] + v[3] + v[4] - | ^^^^ = note: asserting the length before indexing will elide bounds checks = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` @@ -37,182 +12,82 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4] error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 | -LL | let _ = v[0]; - | _____________^ -... | -LL | | let _ = v[1..4]; - | |___________________^ - | - = help: consider asserting the length before indexing: `assert!(v.len() > 3);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 - | LL | let _ = v[0]; | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:13:13 - | +... LL | let _ = v[1..4]; | ^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 3);` error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 | -LL | let a = v[0]; - | _____________^ -LL | | -LL | | -LL | | let b = v[1]; -LL | | let c = v[2]; - | |________________^ - | - = help: consider asserting the length before indexing: `assert!(v.len() > 2);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 - | LL | let a = v[0]; | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:20:13 - | +... LL | let b = v[1]; | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:21:13 - | LL | let c = v[2]; | ^^^^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 2);` error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 | LL | let _ = v1[0] + v1[12]; - | ^^^^^^^^^^^^^^ + | ^^^^^ ^^^^^^ | = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:21 - | -LL | let _ = v1[0] + v1[12]; - | ^^^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 | LL | let _ = v2[5] + v2[15]; - | ^^^^^^^^^^^^^^ + | ^^^^^ ^^^^^^ | = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:21 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 | LL | let _ = v2[5] + v2[15]; - | ^^^^^^^^^^^^^^ + | ^^^^^ ^^^^^^ | = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:21 - | -LL | let _ = v2[5] + v2[15]; - | ^^^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 | LL | let _ = f.v[0] + f.v[1]; - | ^^^^^^^^^^^^^^^ + | ^^^^^^ ^^^^^^ | = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 - | -LL | let _ = f.v[0] + f.v[1]; - | ^^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:22 - | -LL | let _ = f.v[0] + f.v[1]; - | ^^^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 | LL | let _ = x[0] + x[1]; - | ^^^^^^^^^^^ + | ^^^^ ^^^^ | = help: consider asserting the length before indexing: `assert!(x.len() > 1);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 - | -LL | let _ = x[0] + x[1]; - | ^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:20 - | -LL | let _ = x[0] + x[1]; - | ^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 | LL | let _ = v1[1] + v1[2]; - | ^^^^^^^^^^^^^ + | ^^^^^ ^^^^^ | = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 - | -LL | let _ = v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:21 - | -LL | let _ = v1[1] + v1[2]; - | ^^^^^ error: indexing into a slice multiple times without an `assert` --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 | LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ ^^^^^ ^^^^^ | = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:21 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ -note: slice indexed here - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:29 - | -LL | let _ = v1[0] + v1[1] + v1[2]; - | ^^^^^ error: aborting due to 10 previous errors From f3b905d4a84b4d7ef58bcda3e2dcff08df979d2c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 20 Nov 2025 13:46:55 +0100 Subject: [PATCH 10/56] use `span_suggestion_verbose` --- .../src/missing_asserts_for_indexing.rs | 2 +- tests/ui/missing_asserts_for_indexing.stderr | 126 +++++++++++++----- 2 files changed, 90 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index a89a460cdfb3..72bfe830da81 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -401,7 +401,7 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> indexes, "indexing into a slice multiple times with an `assert` that does not cover the highest index", |diag| { - diag.span_suggestion( + diag.span_suggestion_verbose( assert_span, "provide the highest index that is indexed with", sugg, diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr index 30a35a21e5d3..d5b5455343e4 100644 --- a/tests/ui/missing_asserts_for_indexing.stderr +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -1,139 +1,191 @@ error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:30:5 | -LL | assert!(v.len() < 5); - | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ | = note: asserting the length before indexing will elide bounds checks = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` +help: provide the highest index that is indexed with + | +LL - assert!(v.len() < 5); +LL + assert!(v.len() > 4); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:36:5 | -LL | assert!(v.len() <= 5); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v.len() <= 5); +LL + assert!(v.len() > 4); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:42:5 | -LL | assert!(v.len() > 3); - | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v.len() > 3); +LL + assert!(v.len() > 4); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:48:5 | -LL | assert!(v.len() >= 4); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v.len() >= 4); +LL + assert!(v.len() > 4); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:66:13 | -LL | assert!(v.len() >= 3); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)` LL | let _ = v[0]; | ^^^^ ... LL | let _ = v[1..4]; | ^^^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v.len() >= 3); +LL + assert!(v.len() > 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:81:13 | -LL | assert!(v.len() >= 4); - | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` LL | let _ = v[0]; | ^^^^ ... LL | let _ = v[1..=4]; | ^^^^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v.len() >= 4); +LL + assert!(v.len() > 4); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:97:13 | -LL | assert!(v1.len() >= 12); - | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` -LL | assert!(v2.len() >= 15); LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v1.len() >= 12); +LL + assert!(v1.len() > 12); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:100:13 | -LL | assert!(v2.len() >= 15); - | ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)` -... LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v2.len() >= 15); +LL + assert!(v2.len() > 15); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:106:13 | -LL | assert!(v1.len() >= 12); - | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` -LL | assert!(v2.len() > 15); LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v1.len() >= 12); +LL + assert!(v1.len() > 12); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:131:13 | -LL | assert!(v1.len() == 2); - | ---------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` -... LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(v1.len() == 2); +LL + assert!(v1.len() == 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:136:13 | -LL | assert!(2 == v3.len()); - | ---------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` -... LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ ^^^^^ ^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert!(2 == v3.len()); +LL + assert!(v3.len() == 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:158:13 | -LL | assert_eq!(v1.len(), 2); - | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)` -... LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert_eq!(v1.len(), 2); +LL + assert_eq!(v1.len(), 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:163:13 | -LL | assert_eq!(2, v3.len()); - | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)` -... LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ ^^^^^ ^^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert_eq!(2, v3.len()); +LL + assert_eq!(v3.len(), 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:172:17 | -LL | assert_eq!(v.len(), 2); - | ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)` LL | let _ = v[0] + v[1] + v[2]; | ^^^^ ^^^^ ^^^^ + | +help: provide the highest index that is indexed with + | +LL - assert_eq!(v.len(), 2); +LL + assert_eq!(v.len(), 3); + | error: indexing into a slice multiple times with an `assert` that does not cover the highest index --> tests/ui/missing_asserts_for_indexing.rs:178:17 | -LL | debug_assert_eq!(v.len(), 2); - | ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)` LL | let _ = v[0] + v[1] + v[2]; | ^^^^ ^^^^ ^^^^ + | +help: provide the highest index that is indexed with + | +LL - debug_assert_eq!(v.len(), 2); +LL + debug_assert_eq!(v.len(), 3); + | error: aborting due to 15 previous errors From 129a39d6be3e18ea1ad3daae992901e1a27f7851 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 20 Nov 2025 14:53:19 +0100 Subject: [PATCH 11/56] fix: adjust the applicability for the suggestion --- .../src/missing_asserts_for_indexing.rs | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 72bfe830da81..87ee164a1760 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -5,7 +5,7 @@ use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::{If, Range}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr}; use rustc_ast::{BinOpKind, LitKind, RangeLimits}; @@ -356,41 +356,33 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> slice, macro_call, } if indexes.len() > 1 && !is_first_highest => { + let mut app = Applicability::MachineApplicable; + let slice_str = snippet_with_applicability(cx, slice.span, "_", &mut app); // if we have found an `assert!`, let's also check that it's actually right // and if it covers the highest index and if not, suggest the correct length let sugg = match comparison { // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. // The user probably meant `v.len() > 5` - LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some( - format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")), - ), + LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => { + Some(format!("assert!({slice_str}.len() > {highest_index})",)) + }, // `5 < v.len()` == `v.len() > 5` - LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!( - "assert!({}.len() > {highest_index})", - snippet(cx, slice.span, "..") - )), + LengthComparison::IntLessThanLength if asserted_len < highest_index => { + Some(format!("assert!({slice_str}.len() > {highest_index})",)) + }, // `5 <= v.len() == `v.len() >= 5` - LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!( - "assert!({}.len() > {highest_index})", - snippet(cx, slice.span, "..") - )), + LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => { + Some(format!("assert!({slice_str}.len() > {highest_index})",)) + }, // `highest_index` here is rather a length, so we need to add 1 to it LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call { - sym::assert_eq_macro => Some(format!( - "assert_eq!({}.len(), {})", - snippet(cx, slice.span, ".."), - highest_index + 1 - )), - sym::debug_assert_eq_macro => Some(format!( - "debug_assert_eq!({}.len(), {})", - snippet(cx, slice.span, ".."), - highest_index + 1 - )), - _ => Some(format!( - "assert!({}.len() == {})", - snippet(cx, slice.span, ".."), - highest_index + 1 - )), + sym::assert_eq_macro => { + Some(format!("assert_eq!({slice_str}.len(), {})", highest_index + 1)) + }, + sym::debug_assert_eq_macro => { + Some(format!("debug_assert_eq!({slice_str}.len(), {})", highest_index + 1)) + }, + _ => Some(format!("assert!({slice_str}.len() == {})", highest_index + 1)), }, _ => None, }; @@ -405,7 +397,7 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> assert_span, "provide the highest index that is indexed with", sugg, - Applicability::MachineApplicable, + app, ); }, ); From e6ad406d48956b2b3f159d116af2d05cda5da79a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 11 Oct 2025 13:13:48 +0200 Subject: [PATCH 12/56] feat(manual_ilog2): new lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_ilog2.rs | 115 +++++++++++++++++++++++++++++ clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/sym.rs | 2 + tests/ui/manual_ilog2.fixed | 32 ++++++++ tests/ui/manual_ilog2.rs | 32 ++++++++ tests/ui/manual_ilog2.stderr | 23 ++++++ 9 files changed, 209 insertions(+) create mode 100644 clippy_lints/src/manual_ilog2.rs create mode 100644 tests/ui/manual_ilog2.fixed create mode 100644 tests/ui/manual_ilog2.rs create mode 100644 tests/ui/manual_ilog2.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b81b5b74d6..76de222960e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6541,6 +6541,7 @@ Released 2018-09-13 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one [`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp +[`manual_ilog2`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ilog2 [`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4a350dca2993..edaea69d01a5 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -300,6 +300,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO, + crate::manual_ilog2::MANUAL_ILOG2_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 230d83dacc95..cad36b7f197a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -201,6 +201,7 @@ mod manual_clamp; mod manual_float_methods; mod manual_hash_one; mod manual_ignore_case_cmp; +mod manual_ilog2; mod manual_is_ascii_check; mod manual_is_power_of_two; mod manual_let_else; @@ -848,6 +849,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)), Box::new(|_| Box::new(volatile_composites::VolatileComposites)), Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))), // add late passes here, used by `cargo dev new_lint` ]; store.late_passes.extend(late_lints); diff --git a/clippy_lints/src/manual_ilog2.rs b/clippy_lints/src/manual_ilog2.rs new file mode 100644 index 000000000000..1c61db530606 --- /dev/null +++ b/clippy_lints/src/manual_ilog2.rs @@ -0,0 +1,115 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_from_proc_macro, sym}; +use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions like `N - x.leading_zeros()` (where `N` is one less than bit width + /// of `x`) or `x.ilog(2)`, which are manual reimplementations of `x.ilog2()` + /// + /// ### Why is this bad? + /// Manual reimplementations of `ilog2` increase code complexity for little benefit. + /// + /// ### Example + /// ```no_run + /// let x: u32 = 5; + /// let log = 31 - x.leading_zeros(); + /// let log = x.ilog(2); + /// ``` + /// Use instead: + /// ```no_run + /// let x: u32 = 5; + /// let log = x.ilog2(); + /// let log = x.ilog2(); + /// ``` + #[clippy::version = "1.93.0"] + pub MANUAL_ILOG2, + pedantic, + "manually reimplementing `ilog2`" +} + +pub struct ManualIlog2 { + msrv: Msrv, +} + +impl ManualIlog2 { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +impl_lint_pass!(ManualIlog2 => [MANUAL_ILOG2]); + +impl LateLintPass<'_> for ManualIlog2 { + fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if expr.span.in_external_macro(cx.sess().source_map()) { + return; + } + + match expr.kind { + // `BIT_WIDTH - 1 - n.leading_zeros()` + ExprKind::Binary(op, left, right) + if left.span.eq_ctxt(right.span) + && op.node == BinOpKind::Sub + && let ExprKind::Lit(lit) = left.kind + && let LitKind::Int(Pu128(val), _) = lit.node + && let ExprKind::MethodCall(leading_zeros, recv, [], _) = right.kind + && leading_zeros.ident.name == sym::leading_zeros + && let ty = cx.typeck_results().expr_ty(recv) + && let Some(bit_width) = match ty.kind() { + ty::Uint(uint_ty) => uint_ty.bit_width(), + ty::Int(_) => { + // On non-positive integers, `ilog2` would panic, which might be a sign that the author does + // in fact want to calculate something different, so stay on the safer side and don't + // suggest anything. + return; + }, + _ => return, + } + && val == u128::from(bit_width) - 1 + && self.msrv.meets(cx, msrvs::ILOG2) + && !is_from_proc_macro(cx, expr) => + { + emit(cx, recv, expr); + }, + + // `n.ilog(2)` + ExprKind::MethodCall(ilog, recv, [two], _) + if expr.span.eq_ctxt(two.span) + && ilog.ident.name == sym::ilog + && let ExprKind::Lit(lit) = two.kind + && let LitKind::Int(Pu128(2), _) = lit.node + && cx.typeck_results().expr_ty_adjusted(recv).is_integral() + /* no need to check MSRV here, as `ilog` and `ilog2` were introduced simultaneously */ + && !is_from_proc_macro(cx, expr) => + { + emit(cx, recv, expr); + }, + + _ => {}, + } + } +} + +fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, full_expr: &Expr<'_>) { + let mut app = Applicability::MachineApplicable; + let recv = snippet_with_applicability(cx, recv.span, "_", &mut app); + span_lint_and_sugg( + cx, + MANUAL_ILOG2, + full_expr.span, + "manually reimplementing `ilog2`", + "try", + format!("{recv}.ilog2()"), + app, + ); +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 86d17a8231d5..4a7fa3472cae 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -40,6 +40,7 @@ msrv_aliases! { 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } + 1,67,0 { ILOG2 } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 1d1537dd0e91..00f4a9c7e586 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -180,6 +180,7 @@ generate! { has_significant_drop, hidden_glob_reexports, hygiene, + ilog, insert, insert_str, inspect, @@ -207,6 +208,7 @@ generate! { join, kw, lazy_static, + leading_zeros, lint_vec, ln, lock, diff --git a/tests/ui/manual_ilog2.fixed b/tests/ui/manual_ilog2.fixed new file mode 100644 index 000000000000..a0f6d9392c30 --- /dev/null +++ b/tests/ui/manual_ilog2.fixed @@ -0,0 +1,32 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::manual_ilog2)] +#![allow(clippy::unnecessary_operation)] + +use proc_macros::{external, with_span}; + +fn foo(a: u32, b: u64) { + a.ilog2(); //~ manual_ilog2 + a.ilog2(); //~ manual_ilog2 + + b.ilog2(); //~ manual_ilog2 + 64 - b.leading_zeros(); // No lint because manual ilog2 is `BIT_WIDTH - 1 - x.leading_zeros()` + + // don't lint when macros are involved + macro_rules! two { + () => { + 2 + }; + }; + + macro_rules! thirty_one { + () => { + 31 + }; + }; + + a.ilog(two!()); + thirty_one!() - a.leading_zeros(); + + external!($a.ilog(2)); + with_span!(span; a.ilog(2)); +} diff --git a/tests/ui/manual_ilog2.rs b/tests/ui/manual_ilog2.rs new file mode 100644 index 000000000000..bd4b5d9d3c0d --- /dev/null +++ b/tests/ui/manual_ilog2.rs @@ -0,0 +1,32 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::manual_ilog2)] +#![allow(clippy::unnecessary_operation)] + +use proc_macros::{external, with_span}; + +fn foo(a: u32, b: u64) { + 31 - a.leading_zeros(); //~ manual_ilog2 + a.ilog(2); //~ manual_ilog2 + + 63 - b.leading_zeros(); //~ manual_ilog2 + 64 - b.leading_zeros(); // No lint because manual ilog2 is `BIT_WIDTH - 1 - x.leading_zeros()` + + // don't lint when macros are involved + macro_rules! two { + () => { + 2 + }; + }; + + macro_rules! thirty_one { + () => { + 31 + }; + }; + + a.ilog(two!()); + thirty_one!() - a.leading_zeros(); + + external!($a.ilog(2)); + with_span!(span; a.ilog(2)); +} diff --git a/tests/ui/manual_ilog2.stderr b/tests/ui/manual_ilog2.stderr new file mode 100644 index 000000000000..7c9694f35330 --- /dev/null +++ b/tests/ui/manual_ilog2.stderr @@ -0,0 +1,23 @@ +error: manually reimplementing `ilog2` + --> tests/ui/manual_ilog2.rs:8:5 + | +LL | 31 - a.leading_zeros(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.ilog2()` + | + = note: `-D clippy::manual-ilog2` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ilog2)]` + +error: manually reimplementing `ilog2` + --> tests/ui/manual_ilog2.rs:9:5 + | +LL | a.ilog(2); + | ^^^^^^^^^ help: try: `a.ilog2()` + +error: manually reimplementing `ilog2` + --> tests/ui/manual_ilog2.rs:11:5 + | +LL | 63 - b.leading_zeros(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `b.ilog2()` + +error: aborting due to 3 previous errors + From 110649f0a8dfb21e65ee8114c05eacf5260307b1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Nov 2025 13:09:08 +0100 Subject: [PATCH 13/56] Fix display of dropdown menu "buttons" --- util/gh-pages/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 242e2227ed94..ce478a3e18d0 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -637,14 +637,14 @@ pre, hr { display: flex; } -ul.dropdown-menu li.checkbox > button { +#menu-filters ul.dropdown-menu li.checkbox > button { border: 0; width: 100%; background: var(--theme-popup-bg); color: var(--fg); } -ul.dropdown-menu li.checkbox > button:hover { +#menu-filters ul.dropdown-menu li.checkbox > button:hover { background: var(--theme-hover); box-shadow: none; } From 099308af902a1d2bc6104192c276b7e946eb5aae Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 28 Nov 2025 20:25:22 +0100 Subject: [PATCH 14/56] Merge commit '92b4b68683249c781c3acad742fc6e57c4140ad9' into clippy-subtree-update --- .github/workflows/clippy_mq.yml | 10 +- .github/workflows/deploy.yml | 4 +- .github/workflows/lintcheck.yml | 6 +- .github/workflows/remark.yml | 7 +- CHANGELOG.md | 2 + .../continuous_integration/github_actions.md | 2 +- book/src/lint_configuration.md | 11 + clippy_config/src/conf.rs | 4 + clippy_lints/src/byte_char_slices.rs | 51 +- clippy_lints/src/declared_lints.rs | 3 +- clippy_lints/src/dereference.rs | 460 ++++--- .../doc/doc_paragraphs_missing_punctuation.rs | 126 ++ clippy_lints/src/doc/mod.rs | 38 + clippy_lints/src/equatable_if_let.rs | 80 +- clippy_lints/src/functions/mod.rs | 30 +- clippy_lints/src/functions/result.rs | 30 +- clippy_lints/src/implicit_hasher.rs | 40 +- clippy_lints/src/lib.rs | 4 +- .../src/methods/sliced_string_as_bytes.rs | 10 +- clippy_lints/src/methods/useless_asref.rs | 28 +- clippy_lints/src/missing_doc.rs | 443 ++++--- .../src/multiple_unsafe_ops_per_block.rs | 115 +- clippy_lints/src/ptr/ptr_arg.rs | 6 +- clippy_lints/src/time_subtraction.rs | 68 +- .../src/transmute/transmute_ptr_to_ptr.rs | 66 +- .../src/transmute/transmute_ref_to_ref.rs | 42 +- clippy_lints/src/unwrap.rs | 229 +++- clippy_lints/src/{vec.rs => useless_vec.rs} | 35 +- clippy_utils/README.md | 2 +- clippy_utils/src/ast_utils/mod.rs | 4 +- clippy_utils/src/check_proc_macro.rs | 9 +- clippy_utils/src/lib.rs | 3 + clippy_utils/src/sugg.rs | 11 +- rust-toolchain.toml | 2 +- src/driver.rs | 7 +- .../missing_docs_allow_unused.rs | 26 - .../missing_docs_allow_unused.stderr | 38 - .../allow_unused}/clippy.toml | 0 .../crate_root}/clippy.toml | 0 .../default/clippy.toml | 0 ..._docs_in_private_items.allow_unused.stderr | 644 +++++++++ ...ng_docs_in_private_items.crate_root.stderr | 500 +++++++ ...ssing_docs_in_private_items.default.stderr | 704 ++++++++++ .../missing_docs_in_private_items.rs | 1167 +++++++++++++++++ .../pub_crate_missing_doc.rs | 67 - .../pub_crate_missing_doc.stderr | 53 - tests/ui-toml/result_large_err/clippy.toml | 1 + .../result_large_err/result_large_err.rs | 20 + .../result_large_err/result_large_err.stderr | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/blanket_clippy_restriction_lints.rs | 2 +- tests/ui/byte_char_slices.fixed | 1 - tests/ui/byte_char_slices.rs | 1 - tests/ui/byte_char_slices.stderr | 10 +- tests/ui/cast.rs | 10 + tests/ui/cast.stderr | 8 +- .../ui/checked_unwrap/complex_conditionals.rs | 28 +- .../complex_conditionals.stderr | 56 +- .../complex_conditionals_nested.rs | 11 +- .../complex_conditionals_nested.stderr | 19 +- tests/ui/checked_unwrap/if_let_chains.rs | 2 +- tests/ui/checked_unwrap/if_let_chains.stderr | 7 +- .../ui/checked_unwrap/simple_conditionals.rs | 225 +++- .../checked_unwrap/simple_conditionals.stderr | 390 ++++-- .../doc_paragraphs_missing_punctuation.fixed | 172 +++ .../doc/doc_paragraphs_missing_punctuation.rs | 172 +++ .../doc_paragraphs_missing_punctuation.stderr | 113 ++ ...aragraphs_missing_punctuation_unfixable.rs | 13 + ...raphs_missing_punctuation_unfixable.stderr | 20 + tests/ui/equatable_if_let.fixed | 107 +- tests/ui/equatable_if_let.rs | 107 +- tests/ui/equatable_if_let.stderr | 52 +- tests/ui/equatable_if_let_const_cmp.fixed | 24 + tests/ui/equatable_if_let_const_cmp.rs | 24 + tests/ui/equatable_if_let_const_cmp.stderr | 17 + tests/ui/explicit_deref_methods.fixed | 113 ++ tests/ui/explicit_deref_methods.rs | 113 ++ tests/ui/implicit_hasher.fixed | 24 + tests/ui/implicit_hasher.rs | 24 + tests/ui/implicit_hasher.stderr | 30 +- tests/ui/missing_doc.rs | 148 --- tests/ui/missing_doc.stderr | 102 -- tests/ui/missing_doc_crate.rs | 7 - tests/ui/missing_doc_crate_missing.rs | 4 - tests/ui/missing_doc_crate_missing.stderr | 13 - tests/ui/missing_doc_impl.rs | 114 -- tests/ui/missing_doc_impl.stderr | 57 - tests/ui/multiple_unsafe_ops_per_block.rs | 103 ++ tests/ui/multiple_unsafe_ops_per_block.stderr | 148 ++- .../redundant_pattern_matching_option.fixed | 29 + tests/ui/redundant_pattern_matching_option.rs | 29 + .../redundant_pattern_matching_option.stderr | 14 +- tests/ui/sliced_string_as_bytes.fixed | 6 + tests/ui/sliced_string_as_bytes.rs | 6 + tests/ui/transmute.rs | 13 + tests/ui/transmute.stderr | 8 +- tests/ui/transmute_ref_to_ref.rs | 20 + tests/ui/transmute_ref_to_ref.stderr | 20 +- tests/ui/unchecked_time_subtraction.stderr | 16 +- ...nchecked_time_subtraction_unfixable.stderr | 8 +- tests/ui/useless_asref.fixed | 35 + tests/ui/useless_asref.rs | 35 + tests/ui/useless_asref.stderr | 32 +- tests/ui/{vec.fixed => useless_vec.fixed} | 38 +- tests/ui/useless_vec.rs | 258 +++- tests/ui/useless_vec.stderr | 132 +- tests/ui/useless_vec_unfixable.rs | 14 + tests/ui/useless_vec_unfixable.stderr | 21 + tests/ui/vec.rs | 253 ---- tests/ui/vec.stderr | 137 -- triagebot.toml | 3 +- util/gh-pages/index_template.html | 216 ++- util/gh-pages/style.css | 79 +- 113 files changed, 6904 insertions(+), 2322 deletions(-) create mode 100644 clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs rename clippy_lints/src/{vec.rs => useless_vec.rs} (90%) delete mode 100644 tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs delete mode 100644 tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr rename tests/ui-toml/{missing_docs_allow_unused => missing_docs_in_private_items/allow_unused}/clippy.toml (100%) rename tests/ui-toml/{pub_crate_missing_docs => missing_docs_in_private_items/crate_root}/clippy.toml (100%) create mode 100644 tests/ui-toml/missing_docs_in_private_items/default/clippy.toml create mode 100644 tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.allow_unused.stderr create mode 100644 tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.crate_root.stderr create mode 100644 tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.default.stderr create mode 100644 tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs delete mode 100644 tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs delete mode 100644 tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr create mode 100644 tests/ui/doc/doc_paragraphs_missing_punctuation.fixed create mode 100644 tests/ui/doc/doc_paragraphs_missing_punctuation.rs create mode 100644 tests/ui/doc/doc_paragraphs_missing_punctuation.stderr create mode 100644 tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs create mode 100644 tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.stderr create mode 100644 tests/ui/equatable_if_let_const_cmp.fixed create mode 100644 tests/ui/equatable_if_let_const_cmp.rs create mode 100644 tests/ui/equatable_if_let_const_cmp.stderr delete mode 100644 tests/ui/missing_doc.rs delete mode 100644 tests/ui/missing_doc.stderr delete mode 100644 tests/ui/missing_doc_crate.rs delete mode 100644 tests/ui/missing_doc_crate_missing.rs delete mode 100644 tests/ui/missing_doc_crate_missing.stderr delete mode 100644 tests/ui/missing_doc_impl.rs delete mode 100644 tests/ui/missing_doc_impl.stderr rename tests/ui/{vec.fixed => useless_vec.fixed} (90%) create mode 100644 tests/ui/useless_vec_unfixable.rs create mode 100644 tests/ui/useless_vec_unfixable.stderr delete mode 100644 tests/ui/vec.rs delete mode 100644 tests/ui/vec.stderr diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index ce15a861bb07..c49241bdff1b 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -34,7 +34,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false @@ -112,7 +112,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false @@ -168,7 +168,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false @@ -179,7 +179,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: binaries path: target/debug diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 48c5bd36dbcd..872931160c35 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 45fd10ae7614..9ce0b7f5fc46 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 2 # Unsetting this would make so that any malicious package could get our Github Token @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -113,7 +113,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index c2cc48ab9511..03641a9aa62f 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -4,6 +4,9 @@ on: merge_group: pull_request: +env: + MDBOOK_VERSION: 0.5.1 + jobs: remark: runs-on: ubuntu-latest @@ -11,7 +14,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -27,7 +30,7 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/mdbook-v${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb2755be0ee..78b81b5b74d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6314,6 +6314,7 @@ Released 2018-09-13 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items +[`doc_paragraphs_missing_punctuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_paragraphs_missing_punctuation [`doc_suspicious_footnotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_suspicious_footnotes [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last @@ -7121,6 +7122,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`inherent-impl-lint-scope`]: https://doc.rust-lang.org/clippy/lint_configuration.html#inherent-impl-lint-scope +[`large-error-ignored`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-ignored [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold [`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md index 62d32446d920..bed0f66bab33 100644 --- a/book/src/continuous_integration/github_actions.md +++ b/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 6569bdabf115..2e185fb3a086 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -681,6 +681,17 @@ Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` * [`multiple_inherent_impl`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl) +## `large-error-ignored` +A list of paths to types that should be ignored as overly large `Err`-variants in a +`Result` returned from a function + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) + + ## `large-error-threshold` The maximum size of the `Err`-variant in a `Result` returned from a function diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8cdd99ac44a8..2e9cf8e91f7d 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -666,6 +666,10 @@ define_Conf! { /// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. #[lints(multiple_inherent_impl)] inherent_impl_lint_scope: InherentImplLintScope = InherentImplLintScope::Crate, + /// A list of paths to types that should be ignored as overly large `Err`-variants in a + /// `Result` returned from a function + #[lints(result_large_err)] + large_error_ignored: Vec = Vec::default(), /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs index d88c0711b397..fc9931439e93 100644 --- a/clippy_lints/src/byte_char_slices.rs +++ b/clippy_lints/src/byte_char_slices.rs @@ -31,8 +31,8 @@ declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]); impl EarlyLintPass for ByteCharSlice { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some(slice) = is_byte_char_slices(expr) - && !expr.span.from_expansion() + if !expr.span.from_expansion() + && let Some(slice) = is_byte_char_slices(expr) { span_lint_and_sugg( cx, @@ -47,33 +47,28 @@ impl EarlyLintPass for ByteCharSlice { } } +/// Checks whether the slice is that of byte chars, and if so, builds a byte-string out of it fn is_byte_char_slices(expr: &Expr) -> Option { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind { - match &expr.kind { - ExprKind::Array(members) => { - if members.is_empty() { - return None; - } - - members - .iter() - .map(|member| match &member.kind { - ExprKind::Lit(Lit { - kind: LitKind::Byte, - symbol, - .. - }) => Some(symbol.as_str()), - _ => None, - }) - .map(|maybe_quote| match maybe_quote { - Some("\"") => Some("\\\""), - Some("\\'") => Some("'"), - other => other, - }) - .collect::>() - }, - _ => None, - } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind + && let ExprKind::Array(members) = &expr.kind + && !members.is_empty() + { + members + .iter() + .map(|member| match &member.kind { + ExprKind::Lit(Lit { + kind: LitKind::Byte, + symbol, + .. + }) => Some(symbol.as_str()), + _ => None, + }) + .map(|maybe_quote| match maybe_quote { + Some("\"") => Some("\\\""), + Some("\\'") => Some("'"), + other => other, + }) + .collect::>() } else { None } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a754eea31165..4a350dca2993 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -118,6 +118,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::doc::DOC_MARKDOWN_INFO, crate::doc::DOC_NESTED_REFDEFS_INFO, crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO, + crate::doc::DOC_PARAGRAPHS_MISSING_PUNCTUATION_INFO, crate::doc::DOC_SUSPICIOUS_FOOTNOTES_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::MISSING_ERRORS_DOC_INFO, @@ -777,7 +778,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::use_self::USE_SELF_INFO, crate::useless_concat::USELESS_CONCAT_INFO, crate::useless_conversion::USELESS_CONVERSION_INFO, - crate::vec::USELESS_VEC_INFO, + crate::useless_vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, crate::visibility::NEEDLESS_PUB_SELF_INFO, crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 548f03c9f205..32fd4afb122e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -12,8 +12,8 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, - Pat, PatKind, Path, QPath, TyKind, UnOp, + self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Item, MatchSource, Mutability, + Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -27,6 +27,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for explicit `deref()` or `deref_mut()` method calls. /// + /// Doesn't lint inside the implementation of the `Deref` or `DerefMut` traits. + /// /// ### Why is this bad? /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise, /// when not part of a method chain. @@ -169,6 +171,10 @@ pub struct Dereferencing<'tcx> { /// /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + + /// The outermost `impl Deref` we're currently in. While we're in one, + /// `explicit_deref_methods` is deactivated + outermost_deref_impl: Option, } #[derive(Debug)] @@ -246,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Stop processing sub expressions when a macro call is seen if expr.span.from_expansion() { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, cx.typeck_results()); + self.report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -255,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, typeck); + self.report(cx, expr, state, data, typeck); } return; }; @@ -263,7 +269,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { if is_from_proc_macro(cx, expr) { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, cx.typeck_results()); + self.report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -515,7 +521,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { let adjusted_ty = data.adjusted_ty; let stability = state.stability; - report(cx, expr, State::DerefedBorrow(state), data, typeck); + self.report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { self.state = Some(( State::Borrow { mutability }, @@ -530,7 +536,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let adjusted_ty = data.adjusted_ty; let stability = state.stability; let for_field_access = state.for_field_access; - report(cx, expr, State::DerefedBorrow(state), data, typeck); + self.report(cx, expr, State::DerefedBorrow(state), data, typeck); if let Some(name) = for_field_access && let sub_expr_ty = typeck.expr_ty(sub_expr) && !ty_contains_field(sub_expr_ty, name) @@ -602,7 +608,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, - (Some((state, data)), _) => report(cx, expr, state, data, typeck), + (Some((state, data)), _) => self.report(cx, expr, state, data, typeck), } } @@ -673,6 +679,31 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.current_body = None; } } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only check for `impl Deref(Mut)`s if we're not already in one + if !self.in_deref_impl() && is_deref_or_derefmut_impl(cx, item) { + self.outermost_deref_impl = Some(item.owner_id); + } + } + + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + // Only clear `self.outermost_deref_impl` if we're escaping the _outermost_ `impl Deref(Mut)` + if self.outermost_deref_impl == Some(item.owner_id) { + self.outermost_deref_impl = None; + } + } +} + +fn is_deref_or_derefmut_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if let hir::ItemKind::Impl(impl_) = item.kind + && let Some(of_trait) = impl_.of_trait + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() + { + cx.tcx.lang_items().deref_trait() == Some(trait_id) || cx.tcx.lang_items().deref_mut_trait() == Some(trait_id) + } else { + false + } } fn try_parse_ref_op<'tcx>( @@ -930,209 +961,11 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { } } -#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn report<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - state: State, - data: StateData<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, -) { - match state { - State::DerefMethod { - ty_changed_count, - is_ufcs, - mutbl, - } => { - let mut app = Applicability::MachineApplicable; - let (expr_str, expr_is_macro_call) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - let ty = typeck.expr_ty(expr); - let (_, ref_count, _) = peel_and_count_ty_refs(ty); - let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { - // a deref call changing &T -> &U requires two deref operators the first time - // this occurs. One to remove the reference, a second to call the deref impl. - "*".repeat(ty_changed_count + 1) - } else { - "*".repeat(ty_changed_count) - }; - let addr_of_str = if ty_changed_count < ref_count { - // Check if a reborrow from &mut T -> &T is required. - if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - "&*" - } else { - "" - } - } else if mutbl == Mutability::Mut { - "&mut " - } else { - "&" - }; - - let expr_str = if !expr_is_macro_call && is_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix { - Cow::Owned(format!("({expr_str})")) - } else { - expr_str - }; - - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - data.first_expr.span, - match mutbl { - Mutability::Not => "explicit `deref` method call", - Mutability::Mut => "explicit `deref_mut` method call", - }, - "try", - format!("{addr_of_str}{deref_str}{expr_str}"), - app, - ); - }, - State::DerefedBorrow(state) => { - // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, - // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. - if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind - && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind - && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() - { - return; - } - - let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - span_lint_hir_and_then( - cx, - NEEDLESS_BORROW, - data.first_expr.hir_id, - data.first_expr.span, - state.msg, - |diag| { - let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { - Node::Expr(e) => match e.kind { - ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, - ExprKind::Call(..) => { - cx.precedence(expr) < ExprPrecedence::Unambiguous - || matches!(expr.kind, ExprKind::Field(..)) - }, - _ => cx.precedence(expr) < cx.precedence(e), - }, - _ => false, - }; - let is_in_tuple = matches!( - get_parent_expr(cx, data.first_expr), - Some(Expr { - kind: ExprKind::Tup(..), - .. - }) - ); - - let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { - format!("({snip})") - } else { - snip.into() - }; - diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); - }, - ); - }, - State::ExplicitDeref { mutability } => { - if is_block_like(expr) - && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() - && ty.is_sized(cx.tcx, cx.typing_env()) - { - // Rustc bug: auto deref doesn't work on block expression when targeting sized types. - return; - } - - let ty = typeck.expr_ty(expr); - - // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst). - if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() - && dst.is_slice() - { - let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); - if n_src_refs >= 2 && src.is_array() { - return; - } - } - - let (prefix, needs_paren) = match mutability { - Some(mutability) if !ty.is_ref() => { - let prefix = match mutability { - Mutability::Not => "&", - Mutability::Mut => "&mut ", - }; - (prefix, cx.precedence(expr) < ExprPrecedence::Prefix) - }, - None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), - _ => ("", false), - }; - span_lint_hir_and_then( - cx, - EXPLICIT_AUTO_DEREF, - data.first_expr.hir_id, - data.first_expr.span, - "deref which would be done by auto-deref", - |diag| { - let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) { - format!("{prefix}({snip})") - } else { - format!("{prefix}{snip}") - }; - diag.span_suggestion(data.first_expr.span, "try", sugg, app); - }, - ); - }, - State::ExplicitDerefField { - derefs_manually_drop, .. - } => { - let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) - && (derefs_manually_drop - || adjust_derefs_manually_drop( - typeck.expr_adjustments(data.first_expr), - typeck.expr_ty(data.first_expr), - )) { - // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` - // field expressions when the base type is a union and the parent - // expression is also a field access. - // - // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a - // deref through `ManuallyDrop<_>` will not compile. - let parent_id = cx.tcx.parent_hir_id(expr.hir_id); - if parent_id == data.first_expr.hir_id { - return; - } - (cx.tcx.hir_node(parent_id).expect_expr().span, true) - } else { - (expr.span, false) - }; - span_lint_hir_and_then( - cx, - EXPLICIT_AUTO_DEREF, - data.first_expr.hir_id, - data.first_expr.span, - "deref which would be done by auto-deref", - |diag| { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; - let sugg = if needs_parens { - format!("({snip})") - } else { - snip.into_owned() - }; - diag.span_suggestion(data.first_expr.span, "try", sugg, app); - }, - ); - }, - State::Borrow { .. } | State::Reborrow { .. } => (), - } -} - impl<'tcx> Dereferencing<'tcx> { + fn in_deref_impl(&self) -> bool { + self.outermost_deref_impl.is_some() + } + 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) && let Some(pat) = outer_pat @@ -1191,4 +1024,211 @@ impl<'tcx> Dereferencing<'tcx> { } } } + + #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] + fn report( + &self, + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + state: State, + data: StateData<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + ) { + match state { + State::DerefMethod { + ty_changed_count, + is_ufcs, + mutbl, + } => { + if self.in_deref_impl() { + // `deref(_mut)` is fine in an `impl Deref(Mut)` + return; + } + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + let ty = typeck.expr_ty(expr); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" + } else { + "" + } + } else if mutbl == Mutability::Mut { + "&mut " + } else { + "&" + }; + + let expr_str = if !expr_is_macro_call && is_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix { + Cow::Owned(format!("({expr_str})")) + } else { + expr_str + }; + + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.first_expr.span, + match mutbl { + Mutability::Not => "explicit `deref` method call", + Mutability::Mut => "explicit `deref_mut` method call", + }, + "try", + format!("{addr_of_str}{deref_str}{expr_str}"), + app, + ); + }, + State::DerefedBorrow(state) => { + // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, + // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. + if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind + && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind + && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() + { + return; + } + + let mut app = Applicability::MachineApplicable; + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + span_lint_hir_and_then( + cx, + NEEDLESS_BORROW, + data.first_expr.hir_id, + data.first_expr.span, + state.msg, + |diag| { + let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { + Node::Expr(e) => match e.kind { + ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, + ExprKind::Call(..) => { + cx.precedence(expr) < ExprPrecedence::Unambiguous + || matches!(expr.kind, ExprKind::Field(..)) + }, + _ => cx.precedence(expr) < cx.precedence(e), + }, + _ => false, + }; + let is_in_tuple = matches!( + get_parent_expr(cx, data.first_expr), + Some(Expr { + kind: ExprKind::Tup(..), + .. + }) + ); + + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { + format!("({snip})") + } else { + snip.into() + }; + diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); + }, + ); + }, + State::ExplicitDeref { mutability } => { + if is_block_like(expr) + && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() + && ty.is_sized(cx.tcx, cx.typing_env()) + { + // Rustc bug: auto deref doesn't work on block expression when targeting sized types. + return; + } + + let ty = typeck.expr_ty(expr); + + // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst). + if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() + && dst.is_slice() + { + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); + if n_src_refs >= 2 && src.is_array() { + return; + } + } + + let (prefix, needs_paren) = match mutability { + Some(mutability) if !ty.is_ref() => { + let prefix = match mutability { + Mutability::Not => "&", + Mutability::Mut => "&mut ", + }; + (prefix, cx.precedence(expr) < ExprPrecedence::Prefix) + }, + None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), + _ => ("", false), + }; + span_lint_hir_and_then( + cx, + EXPLICIT_AUTO_DEREF, + data.first_expr.hir_id, + data.first_expr.span, + "deref which would be done by auto-deref", + |diag| { + let mut app = Applicability::MachineApplicable; + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) { + format!("{prefix}({snip})") + } else { + format!("{prefix}{snip}") + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); + }, + ); + }, + State::ExplicitDerefField { + derefs_manually_drop, .. + } => { + let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) + && (derefs_manually_drop + || adjust_derefs_manually_drop( + typeck.expr_adjustments(data.first_expr), + typeck.expr_ty(data.first_expr), + )) { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + let parent_id = cx.tcx.parent_hir_id(expr.hir_id); + if parent_id == data.first_expr.hir_id { + return; + } + (cx.tcx.hir_node(parent_id).expect_expr().span, true) + } else { + (expr.span, false) + }; + span_lint_hir_and_then( + cx, + EXPLICIT_AUTO_DEREF, + data.first_expr.hir_id, + data.first_expr.span, + "deref which would be done by auto-deref", + |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; + let sugg = if needs_parens { + format!("({snip})") + } else { + snip.into_owned() + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); + }, + ); + }, + State::Borrow { .. } | State::Reborrow { .. } => (), + } + } } diff --git a/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs b/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs new file mode 100644 index 000000000000..a8f734637672 --- /dev/null +++ b/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs @@ -0,0 +1,126 @@ +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_resolve::rustdoc::main_body_opts; + +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser, Tag, TagEnd}; + +use super::{DOC_PARAGRAPHS_MISSING_PUNCTUATION, Fragments}; + +const MSG: &str = "doc paragraphs should end with a terminal punctuation mark"; +const PUNCTUATION_SUGGESTION: char = '.'; + +pub fn check(cx: &LateContext<'_>, doc: &str, fragments: Fragments<'_>) { + for missing_punctuation in is_missing_punctuation(doc) { + match missing_punctuation { + MissingPunctuation::Fixable(offset) => { + // This ignores `#[doc]` attributes, which we do not handle. + if let Some(span) = fragments.span(cx, offset..offset) { + clippy_utils::diagnostics::span_lint_and_sugg( + cx, + DOC_PARAGRAPHS_MISSING_PUNCTUATION, + span, + MSG, + "end the paragraph with some punctuation", + PUNCTUATION_SUGGESTION.to_string(), + Applicability::MaybeIncorrect, + ); + } + }, + MissingPunctuation::Unfixable(offset) => { + // This ignores `#[doc]` attributes, which we do not handle. + if let Some(span) = fragments.span(cx, offset..offset) { + clippy_utils::diagnostics::span_lint_and_help( + cx, + DOC_PARAGRAPHS_MISSING_PUNCTUATION, + span, + MSG, + None, + "end the paragraph with some punctuation", + ); + } + }, + } + } +} + +#[must_use] +/// If punctuation is missing, returns the offset where new punctuation should be inserted. +fn is_missing_punctuation(doc_string: &str) -> Vec { + // The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that + // introduce a table or a list for example. + const TERMINAL_PUNCTUATION_MARKS: &[char] = &['.', '?', '!', '…', ':']; + + let mut no_report_depth = 0; + let mut missing_punctuation = Vec::new(); + let mut current_paragraph = None; + + for (event, offset) in + Parser::new_ext(doc_string, main_body_opts() - Options::ENABLE_SMART_PUNCTUATION).into_offset_iter() + { + match event { + Event::Start( + Tag::CodeBlock(..) + | Tag::FootnoteDefinition(_) + | Tag::Heading { .. } + | Tag::HtmlBlock + | Tag::List(..) + | Tag::Table(_), + ) => { + no_report_depth += 1; + }, + Event::End(TagEnd::FootnoteDefinition) => { + no_report_depth -= 1; + }, + Event::End( + TagEnd::CodeBlock | TagEnd::Heading(_) | TagEnd::HtmlBlock | TagEnd::List(_) | TagEnd::Table, + ) => { + no_report_depth -= 1; + current_paragraph = None; + }, + Event::InlineHtml(_) | Event::Start(Tag::Image { .. }) | Event::End(TagEnd::Image) => { + current_paragraph = None; + }, + Event::End(TagEnd::Paragraph) => { + if let Some(mp) = current_paragraph { + missing_punctuation.push(mp); + } + }, + Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link) + if no_report_depth == 0 && !offset.is_empty() => + { + if doc_string[..offset.end] + .trim_end() + .ends_with(TERMINAL_PUNCTUATION_MARKS) + { + current_paragraph = None; + } else { + current_paragraph = Some(MissingPunctuation::Fixable(offset.end)); + } + }, + Event::Text(..) if no_report_depth == 0 && !offset.is_empty() => { + let trimmed = doc_string[..offset.end].trim_end(); + if trimmed.ends_with(TERMINAL_PUNCTUATION_MARKS) { + current_paragraph = None; + } else if let Some(t) = trimmed.strip_suffix(|c| c == ')' || c == '"') { + if t.ends_with(TERMINAL_PUNCTUATION_MARKS) { + // Avoid false positives. + current_paragraph = None; + } else { + current_paragraph = Some(MissingPunctuation::Unfixable(offset.end)); + } + } else { + current_paragraph = Some(MissingPunctuation::Fixable(offset.end)); + } + }, + _ => {}, + } + } + + missing_punctuation +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum MissingPunctuation { + Fixable(usize), + Unfixable(usize), +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 1e1d6e69cc91..120da92da944 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -27,6 +27,7 @@ use url::Url; mod broken_link; mod doc_comment_double_space_linebreaks; +mod doc_paragraphs_missing_punctuation; mod doc_suspicious_footnotes; mod include_in_doc_without_cfg; mod lazy_continuation; @@ -670,6 +671,33 @@ declare_clippy_lint! { "looks like a link or footnote ref, but with no definition" } +declare_clippy_lint! { + /// ### What it does + /// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark. + /// Various Markdowns constructs are taken into account to avoid false positives. + /// + /// ### Why is this bad? + /// A project may wish to enforce consistent doc comments by making sure paragraphs end with a + /// punctuation mark. + /// + /// ### Example + /// ```no_run + /// /// Returns a random number + /// /// + /// /// It was chosen by a fair dice roll + /// ``` + /// Use instead: + /// ```no_run + /// /// Returns a random number. + /// /// + /// /// It was chosen by a fair dice roll. + /// ``` + #[clippy::version = "1.93.0"] + pub DOC_PARAGRAPHS_MISSING_PUNCTUATION, + restriction, + "missing terminal punctuation in doc comments" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -704,6 +732,7 @@ impl_lint_pass!(Documentation => [ DOC_INCLUDE_WITHOUT_CFG, DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, DOC_SUSPICIOUS_FOOTNOTES, + DOC_PARAGRAPHS_MISSING_PUNCTUATION, ]); impl EarlyLintPass for Documentation { @@ -875,6 +904,15 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ }, ); + doc_paragraphs_missing_punctuation::check( + cx, + &doc, + Fragments { + doc: &doc, + fragments: &fragments, + }, + ); + // NOTE: check_doc uses it own cb function, // to avoid causing duplicated diagnostics for the broken link checker. let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> { diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 9b5cd7e1731f..a92bfd45df0c 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_in_const_context; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; @@ -40,9 +41,9 @@ declare_clippy_lint! { declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]); /// detects if pattern matches just one thing -fn unary_pattern(pat: &Pat<'_>) -> bool { +fn is_unary_pattern(pat: &Pat<'_>) -> bool { fn array_rec(pats: &[Pat<'_>]) -> bool { - pats.iter().all(unary_pattern) + pats.iter().all(is_unary_pattern) } match &pat.kind { PatKind::Missing => unreachable!(), @@ -53,9 +54,9 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Never | PatKind::Or(_) | PatKind::Err(_) => false, - PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)), + PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| is_unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), + PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => is_unary_pattern(x), PatKind::Expr(_) => true, } } @@ -103,48 +104,63 @@ fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind - && unary_pattern(let_expr.pat) + && is_unary_pattern(let_expr.pat) && !expr.span.in_external_macro(cx.sess().source_map()) + && !let_expr.pat.span.from_expansion() + && !let_expr.init.span.from_expansion() { 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; - if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { - 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( + let mut app = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + + if is_structural_partial_eq(cx, exp_ty, pat_ty) + && !contains_type_mismatch(cx, let_expr.pat) + // Calls to trait methods (`PartialEq::eq` in this case) aren't stable yet. We could _technically_ + // try looking at whether: + // 1) features `const_trait_impl` and `const_cmp` are enabled + // 2) implementation of `PartialEq for ExpTy` has `fn eq` that is `const` + // + // but that didn't quite work out (see #15482), so we just reject outright in this case + && !is_in_const_context(cx) + { + span_lint_and_then( cx, EQUATABLE_IF_LET, expr.span, "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {pat_str}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - applicability, + |diag| { + let pat_str = { + let str = snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0; + if let PatKind::Struct(..) = let_expr.pat.kind { + format!("({str})").into() + } else { + str + } + }; + + let sugg = format!( + "{} == {pat_str}", + snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0, + ); + diag.span_suggestion(expr.span, "try", sugg, app); + }, ); } else { - span_lint_and_sugg( + span_lint_and_then( 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, + |diag| { + let sugg = format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0, + snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0, + ); + diag.span_suggestion(expr.span, "try", sugg, app); + }, ); } } diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 5a40af421942..bdc366f6878a 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -485,6 +485,7 @@ pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, + large_error_ignored: DefIdSet, avoid_breaking_exported_api: bool, /// A set of resolved `def_id` of traits that are configured to allow /// function params renaming. @@ -498,6 +499,11 @@ impl Functions { too_many_arguments_threshold: conf.too_many_arguments_threshold, too_many_lines_threshold: conf.too_many_lines_threshold, large_error_threshold: conf.large_error_threshold, + large_error_ignored: conf + .large_error_ignored + .iter() + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) + .collect(), avoid_breaking_exported_api: conf.avoid_breaking_exported_api, trait_ids: conf .allow_renamed_params_for @@ -554,12 +560,24 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { must_use::check_item(cx, item); - result::check_item(cx, item, self.large_error_threshold, self.msrv); + result::check_item( + cx, + item, + self.large_error_threshold, + &self.large_error_ignored, + self.msrv, + ); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); - result::check_impl_item(cx, item, self.large_error_threshold, self.msrv); + result::check_impl_item( + cx, + item, + self.large_error_threshold, + &self.large_error_ignored, + self.msrv, + ); impl_trait_in_params::check_impl_item(cx, item); renamed_function_params::check_impl_item(cx, item, &self.trait_ids); } @@ -568,7 +586,13 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); - result::check_trait_item(cx, item, self.large_error_threshold, self.msrv); + result::check_trait_item( + cx, + item, + self.large_error_threshold, + &self.large_error_ignored, + self.msrv, + ); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); } diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index fb80cc1a63a3..04e15a1d8a0e 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -4,6 +4,7 @@ use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefIdSet; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; @@ -35,7 +36,13 @@ fn result_err_ty<'tcx>( } } -pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64, msrv: Msrv) { +pub(super) fn check_item<'tcx>( + cx: &LateContext<'tcx>, + item: &hir::Item<'tcx>, + large_err_threshold: u64, + large_err_ignored: &DefIdSet, + msrv: Msrv, +) { if let hir::ItemKind::Fn { ref sig, .. } = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) { @@ -43,7 +50,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, l let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); } } @@ -51,6 +58,7 @@ pub(super) fn check_impl_item<'tcx>( cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64, + large_err_ignored: &DefIdSet, msrv: Msrv, ) { // Don't lint if method is a trait's implementation, we can't do anything about those @@ -62,7 +70,7 @@ pub(super) fn check_impl_item<'tcx>( let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); } } @@ -70,6 +78,7 @@ pub(super) fn check_trait_item<'tcx>( cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64, + large_err_ignored: &DefIdSet, msrv: Msrv, ) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { @@ -78,7 +87,7 @@ pub(super) fn check_trait_item<'tcx>( if cx.effective_visibilities.is_exported(item.owner_id.def_id) { check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); } } } @@ -96,7 +105,18 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } } -fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { +fn check_result_large_err<'tcx>( + cx: &LateContext<'tcx>, + err_ty: Ty<'tcx>, + hir_ty_span: Span, + large_err_threshold: u64, + large_err_ignored: &DefIdSet, +) { + if let ty::Adt(adt, _) = err_ty.kind() + && large_err_ignored.contains(&adt.did()) + { + return; + } if let ty::Adt(adt, subst) = err_ty.kind() && let Some(local_def_id) = adt.did().as_local() && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id) diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d2bc0b6d9935..638a08b096db 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet, snippet_with_context}; use clippy_utils::sym; declare_clippy_lint! { @@ -335,29 +335,29 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { return; } - match (self.cx.tcx.get_diagnostic_name(ty_did), method.ident.name) { - (Some(sym::HashMap), sym::new) => { - self.suggestions.insert(e.span, "HashMap::default()".to_string()); + let container_name = match self.cx.tcx.get_diagnostic_name(ty_did) { + Some(sym::HashMap) => "HashMap", + Some(sym::HashSet) => "HashSet", + _ => return, + }; + + match method.ident.name { + sym::new => { + self.suggestions.insert(e.span, format!("{container_name}::default()")); }, - (Some(sym::HashMap), sym::with_capacity) => { - self.suggestions.insert( - e.span, - format!( - "HashMap::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), + sym::with_capacity => { + let (arg_snippet, _) = snippet_with_context( + self.cx, + args[0].span, + e.span.ctxt(), + "..", + // We can throw-away the applicability here since the whole suggestion is + // marked as `MaybeIncorrect` later. + &mut Applicability::MaybeIncorrect, ); - }, - (Some(sym::HashSet), sym::new) => { - self.suggestions.insert(e.span, "HashSet::default()".to_string()); - }, - (Some(sym::HashSet), sym::with_capacity) => { self.suggestions.insert( e.span, - format!( - "HashSet::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), + format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())",), ); }, _ => {}, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4542105d3277..230d83dacc95 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -386,7 +386,7 @@ mod upper_case_acronyms; mod use_self; mod useless_concat; mod useless_conversion; -mod vec; +mod useless_vec; mod vec_init_then_push; mod visibility; mod volatile_composites; @@ -592,7 +592,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(move |_| Box::new(transmute::Transmute::new(conf))), Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))), Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))), - Box::new(move |_| Box::new(vec::UselessVec::new(conf))), + Box::new(move |_| Box::new(useless_vec::UselessVec::new(conf))), Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))), Box::new(|_| Box::new(strings::StringLitAsBytes)), Box::new(|_| Box::new(derive::Derive)), diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index 4aff194923a6..fb124f3605b9 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -1,15 +1,21 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use super::SLICED_STRING_AS_BYTES; +/// Checks if `index` is any type of range except `RangeFull` (i.e. `..`) +fn is_bounded_range_literal(cx: &LateContext<'_>, index: &Expr<'_>) -> bool { + higher::Range::hir(cx, index).is_some_and(|range| Option::or(range.start, range.end).is_some()) +} + pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind - && is_range_literal(index) + && is_bounded_range_literal(cx, index) && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() && (ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 972304d79e75..f852080d0f2a 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; use clippy_utils::{get_parent_expr, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{self as hir, LangItem, Node}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -69,14 +70,37 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo } } + // Add `*` derefs if the expr is used in a ctor, because automatic derefs don't apply in that case. + let deref = if rcv_depth > res_depth { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + match parent { + Node::ExprField(_) => "*".repeat(rcv_depth - res_depth), + Node::Expr(parent) + if let hir::ExprKind::Call(func, _) = parent.kind + && let (_, Some(path)) = func.opt_res_path() + && matches!(path.res, Res::Def(DefKind::Ctor(_, _), _) | Res::SelfCtor(_)) => + { + "*".repeat(rcv_depth - res_depth) + }, + _ => String::new(), + } + } else { + String::new() + }; + let mut applicability = Applicability::MachineApplicable; + let suggestion = format!( + "{deref}{}", + snippet_with_applicability(cx, recvr.span, "..", &mut applicability) + ); + span_lint_and_sugg( cx, USELESS_ASREF, expr.span, format!("this call to `{call_name}` does nothing"), "try", - snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), + suggestion, applicability, ); } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 1c62caa1c827..ac221743cfd6 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -1,26 +1,18 @@ -// Note: More specifically this lint is largely inspired (aka copied) from -// *rustc*'s -// [`missing_doc`]. -// -// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 -// - use clippy_config::Conf; -use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_from_proc_macro; -use clippy_utils::source::SpanRangeExt; -use rustc_ast::ast::MetaItemInner; -use rustc_hir as hir; -use rustc_hir::Attribute; -use rustc_hir::def::DefKind; +use clippy_utils::{is_doc_hidden, is_from_proc_macro}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{AssocContainer, Visibility}; +use rustc_hir::{ + AttrArgs, Attribute, Body, BodyId, FieldDef, HirId, ImplItem, Item, ItemKind, Node, TraitItem, Variant, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::middle::privacy::Level; +use rustc_middle::ty::Visibility; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::sym; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -37,24 +29,27 @@ declare_clippy_lint! { "detects missing documentation for private members" } -macro_rules! note_prev_span_then_ret { - ($prev_span:expr, $span:expr) => {{ - $prev_span = Some($span); - return; - }}; -} - pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, /// Whether to allow fields starting with an underscore to skip documentation requirements allow_unused: bool, - /// Stack of whether #[doc(hidden)] is set - /// at each level which has lint attributes. - doc_hidden_stack: Vec, - /// Used to keep tracking of the previous item, field or variants etc, to get the search span. - prev_span: Option, + /// The current number of modules since the crate root. + module_depth: u32, + macro_module_depth: u32, + /// The current level of the attribute stack. + attr_depth: u32, + /// What `attr_depth` level the first `doc(hidden)` attribute was seen. This is zero if the + /// attribute hasn't been seen. + doc_hidden_depth: u32, + /// What `attr_depth` level the first `automatically_derived` attribute was seen. This is zero + /// if the attribute hasn't been seen. + automatically_derived_depth: u32, + /// The id of the first body we've seen. + in_body: Option, + /// The module/crate id an item must be visible at to be linted. + require_visibility_at: Option, } impl MissingDoc { @@ -62,113 +57,45 @@ impl MissingDoc { Self { crate_items_only: conf.missing_docs_in_crate_items, allow_unused: conf.missing_docs_allow_unused, - doc_hidden_stack: vec![false], - prev_span: None, + module_depth: 0, + macro_module_depth: 0, + attr_depth: 0, + doc_hidden_depth: 0, + automatically_derived_depth: 0, + in_body: None, + require_visibility_at: None, } } - fn doc_hidden(&self) -> bool { - *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") - } - - fn has_include(meta: Option<&[MetaItemInner]>) -> bool { - if let Some(list) = meta - && let Some(meta) = list.first() - && let Some(name) = meta.ident() - { - name.name == sym::include - } else { - false - } - } - - fn check_missing_docs_attrs( - &self, - cx: &LateContext<'_>, - def_id: LocalDefId, - attrs: &[Attribute], - sp: Span, - article: &'static str, - desc: &'static str, - ) { - // If we're building a test harness, then warning about - // documentation is probably not really relevant right now. - if cx.sess().opts.test { - return; + fn is_missing_docs(&self, cx: &LateContext<'_>, def_id: LocalDefId, hir_id: HirId) -> bool { + if cx.tcx.sess.opts.test { + return false; } - // `#[doc(hidden)]` disables missing_docs check. - if self.doc_hidden() { - return; + match cx.effective_visibilities.effective_vis(def_id) { + None if self.require_visibility_at.is_some() => return false, + None if self.crate_items_only && self.module_depth != 0 => return false, + // `missing_docs` lint uses `Reexported` because rustdoc doesn't render documentation + // for items without a reachable path. + Some(vis) if vis.is_public_at_level(Level::Reexported) => return false, + Some(vis) => { + if self.crate_items_only { + // Use the `Reachable` level since rustdoc will be able to render the documentation + // when building private docs. + let vis = vis.at_level(Level::Reachable); + if !(vis.is_public() || matches!(vis, Visibility::Restricted(id) if id.is_top_level_module())) { + return false; + } + } else if let Some(id) = self.require_visibility_at + && !vis.at_level(Level::Reexported).is_accessible_from(id, cx.tcx) + { + return false; + } + }, + None => {}, } - if sp.from_expansion() { - return; - } - - if self.crate_items_only && def_id != CRATE_DEF_ID { - let vis = cx.tcx.visibility(def_id); - if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) { - return; - } - } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) { - return; - } - - if let Some(parent_def_id) = cx.tcx.opt_parent(def_id.to_def_id()) - && let DefKind::AnonConst - | DefKind::AssocConst - | DefKind::AssocFn - | DefKind::Closure - | DefKind::Const - | DefKind::Fn - | DefKind::InlineConst - | DefKind::Static { .. } - | DefKind::SyntheticCoroutineBody = cx.tcx.def_kind(parent_def_id) - { - // Nested item has no generated documentation, so it doesn't need to be documented. - return; - } - - let has_doc = attrs - .iter() - .any(|a| a.doc_str().is_some() || Self::has_include(a.meta_item_list().as_deref())) - || matches!(self.search_span(sp), Some(span) if span_to_snippet_contains_docs(cx, span)); - - if !has_doc { - span_lint( - cx, - MISSING_DOCS_IN_PRIVATE_ITEMS, - sp, - format!("missing documentation for {article} {desc}"), - ); - } - } - - /// Return a span to search for doc comments manually. - /// - /// # Example - /// ```ignore - /// fn foo() { ... } - /// ^^^^^^^^^^^^^^^^ prev_span - /// ↑ - /// | search_span | - /// ↓ - /// fn bar() { ... } - /// ^^^^^^^^^^^^^^^^ cur_span - /// ``` - fn search_span(&self, cur_span: Span) -> Option { - let prev_span = self.prev_span?; - let start_pos = if prev_span.contains(cur_span) { - // In case when the prev_span is an entire struct, or enum, - // and the current span is a field, or variant, we need to search from - // the starting pos of the previous span. - prev_span.lo() - } else { - prev_span.hi() - }; - let search_span = cur_span.with_lo(start_pos).with_hi(cur_span.lo()); - Some(search_span) + !cx.tcx.hir_attrs(hir_id).iter().any(is_doc_attr) } } @@ -176,111 +103,195 @@ impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_attributes(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - let doc_hidden = self.doc_hidden() || is_doc_hidden(attrs); - self.doc_hidden_stack.push(doc_hidden); + self.attr_depth += 1; + if self.doc_hidden_depth == 0 && is_doc_hidden(attrs) { + self.doc_hidden_depth = self.attr_depth; + } } fn check_attributes_post(&mut self, _: &LateContext<'tcx>, _: &'tcx [Attribute]) { - self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); + self.attr_depth -= 1; + if self.attr_depth < self.doc_hidden_depth { + self.doc_hidden_depth = 0; + } + if self.attr_depth < self.automatically_derived_depth { + self.automatically_derived_depth = 0; + } } - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - let attrs = cx.tcx.hir_attrs(hir::CRATE_HIR_ID); - self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); - } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if self.doc_hidden_depth != 0 || self.automatically_derived_depth != 0 || self.in_body.is_some() { + return; + } - fn check_crate_post(&mut self, _: &LateContext<'tcx>) { - self.prev_span = None; - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - match it.kind { - hir::ItemKind::Fn { ident, .. } => { - // ignore main() - if ident.name == sym::main { - let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; - if at_root { - note_prev_span_then_ret!(self.prev_span, it.span); - } + let span = match item.kind { + // ignore main() + ItemKind::Fn { ident, .. } + if ident.name == sym::main && cx.tcx.local_parent(item.owner_id.def_id) == CRATE_DEF_ID => + { + return; + }, + ItemKind::Const(ident, ..) if ident.name == kw::Underscore => return, + ItemKind::Impl { .. } => { + if cx.tcx.is_automatically_derived(item.owner_id.def_id.to_def_id()) { + self.automatically_derived_depth = self.attr_depth; } + return; }, - hir::ItemKind::Const(ident, ..) => { - if ident.name == kw::Underscore { - note_prev_span_then_ret!(self.prev_span, it.span); + ItemKind::ExternCrate(..) + | ItemKind::ForeignMod { .. } + | ItemKind::GlobalAsm { .. } + | ItemKind::Use(..) => return, + + ItemKind::Mod(ident, ..) => { + if item.span.from_expansion() && item.span.eq_ctxt(ident.span) { + self.module_depth += 1; + self.require_visibility_at = cx.tcx.opt_local_parent(item.owner_id.def_id); + self.macro_module_depth = self.module_depth; + return; } + ident.span }, - hir::ItemKind::Enum(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) => {}, - hir::ItemKind::ExternCrate(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), - } - let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); + ItemKind::Const(ident, ..) + | ItemKind::Enum(ident, ..) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Struct(ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) + | ItemKind::TraitAlias(_, ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) => ident.span, + }; - let attrs = cx.tcx.hir_attrs(it.hir_id()); - if !is_from_proc_macro(cx, it) { - self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc); - } - self.prev_span = Some(it.span); - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id()); - - let attrs = cx.tcx.hir_attrs(trait_item.hir_id()); - if !is_from_proc_macro(cx, trait_item) { - self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc); - } - self.prev_span = Some(trait_item.span); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - // If the method is an impl for a trait, don't doc. - match cx.tcx.associated_item(impl_item.owner_id).container { - AssocContainer::Trait | AssocContainer::TraitImpl(_) => { - note_prev_span_then_ret!(self.prev_span, impl_item.span); - }, - AssocContainer::InherentImpl => {}, - } - - let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); - let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - if !is_from_proc_macro(cx, impl_item) { - self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc); - } - self.prev_span = Some(impl_item.span); - } - - fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { - if !(sf.is_positional() - || is_from_proc_macro(cx, sf) - || self.allow_unused && sf.ident.as_str().starts_with('_')) + if !item.span.from_expansion() + && self.is_missing_docs(cx, item.owner_id.def_id, item.hir_id()) + && !is_from_proc_macro(cx, item) { - let attrs = cx.tcx.hir_attrs(sf.hir_id); - self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); + let (article, desc) = cx.tcx.article_and_description(item.owner_id.to_def_id()); + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + span, + format!("missing documentation for {article} {desc}"), + ); + } + if matches!(item.kind, ItemKind::Mod(..)) { + self.module_depth += 1; } - self.prev_span = Some(sf.span); } - fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - let attrs = cx.tcx.hir_attrs(v.hir_id); - if !is_from_proc_macro(cx, v) { - self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant"); + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if matches!(item.kind, ItemKind::Mod(..)) + && self.doc_hidden_depth == 0 + && self.automatically_derived_depth == 0 + && self.in_body.is_none() + { + self.module_depth -= 1; + if self.module_depth < self.macro_module_depth { + self.require_visibility_at = None; + } + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if self.doc_hidden_depth == 0 + && self.automatically_derived_depth == 0 + && self.in_body.is_none() + && !item.span.from_expansion() + && self.is_missing_docs(cx, item.owner_id.def_id, item.hir_id()) + && !is_from_proc_macro(cx, item) + { + let (article, desc) = cx.tcx.article_and_description(item.owner_id.to_def_id()); + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + item.ident.span, + format!("missing documentation for {article} {desc}"), + ); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if self.doc_hidden_depth == 0 + && self.automatically_derived_depth == 0 + && self.in_body.is_none() + && let Node::Item(parent) = cx.tcx.parent_hir_node(item.hir_id()) + && let ItemKind::Impl(impl_) = parent.kind + && impl_.of_trait.is_none() + && !item.span.from_expansion() + && self.is_missing_docs(cx, item.owner_id.def_id, item.hir_id()) + && !is_from_proc_macro(cx, item) + { + let (article, desc) = cx.tcx.article_and_description(item.owner_id.to_def_id()); + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + item.ident.span, + format!("missing documentation for {article} {desc}"), + ); + } + } + + fn check_body(&mut self, _: &LateContext<'tcx>, body: &Body<'tcx>) { + if self.doc_hidden_depth == 0 && self.automatically_derived_depth == 0 && self.in_body.is_none() { + self.in_body = Some(body.id()); + } + } + + fn check_body_post(&mut self, _: &LateContext<'tcx>, body: &Body<'tcx>) { + if self.in_body == Some(body.id()) { + self.in_body = None; + } + } + + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx FieldDef<'_>) { + if self.doc_hidden_depth == 0 + && self.automatically_derived_depth == 0 + && self.in_body.is_none() + && !field.is_positional() + && !field.span.from_expansion() + && !(self.allow_unused && field.ident.name.as_str().starts_with('_')) + && self.is_missing_docs(cx, field.def_id, field.hir_id) + && !is_from_proc_macro(cx, field) + { + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + field.ident.span, + "missing documentation for a field", + ); + } + } + + fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx Variant<'_>) { + if self.doc_hidden_depth == 0 + && self.automatically_derived_depth == 0 + && self.in_body.is_none() + && !variant.span.from_expansion() + && self.is_missing_docs(cx, variant.def_id, variant.hir_id) + && !is_from_proc_macro(cx, variant) + { + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + variant.ident.span, + "missing documentation for a variant", + ); } - self.prev_span = Some(v.span); } } -fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool { - search_span.check_source_text(cx, |src| src.lines().rev().any(|line| line.trim().starts_with("///"))) +fn is_doc_attr(attr: &Attribute) -> bool { + match attr { + Attribute::Parsed(AttributeKind::DocComment { .. }) => true, + Attribute::Unparsed(attr) + if let [ident] = &*attr.path.segments + && ident.name == sym::doc => + { + matches!(attr.args, AttrArgs::Eq { .. }) + }, + _ => false, + } } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index bc5e72270f4e..80cf081992cc 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,13 +1,13 @@ use clippy_utils::desugar_await; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; -use core::ops::ControlFlow::Continue; use hir::def::{DefKind, Res}; use hir::{BlockCheckMode, ExprKind, QPath, UnOp}; -use rustc_ast::Mutability; +use rustc_ast::{BorrowKind, Mutability}; use rustc_hir as hir; +use rustc_hir::intravisit::{Visitor, walk_body, walk_expr}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, TyCtxt, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::{DesugaringKind, Span}; @@ -55,6 +55,13 @@ declare_clippy_lint! { /// unsafe { char::from_u32_unchecked(int_value) } /// } /// ``` + /// + /// ### Note + /// + /// Taking a raw pointer to a union field is always safe and will + /// not be considered unsafe by this lint, even when linting code written + /// with a specified Rust version of 1.91 or earlier (which required + /// using an `unsafe` block). #[clippy::version = "1.69.0"] pub MULTIPLE_UNSAFE_OPS_PER_BLOCK, restriction, @@ -70,8 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { { return; } - let mut unsafe_ops = vec![]; - collect_unsafe_exprs(cx, block, &mut unsafe_ops); + let unsafe_ops = UnsafeExprCollector::collect_unsafe_exprs(cx, block); if unsafe_ops.len() > 1 { span_lint_and_then( cx, @@ -91,25 +97,49 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { } } -fn collect_unsafe_exprs<'tcx>( - cx: &LateContext<'tcx>, - node: impl Visitable<'tcx>, - unsafe_ops: &mut Vec<(&'static str, Span)>, -) { - for_each_expr(cx, node, |expr| { +struct UnsafeExprCollector<'tcx> { + tcx: TyCtxt<'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, + unsafe_ops: Vec<(&'static str, Span)>, +} + +impl<'tcx> UnsafeExprCollector<'tcx> { + fn collect_unsafe_exprs(cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) -> Vec<(&'static str, Span)> { + let mut collector = Self { + tcx: cx.tcx, + typeck_results: cx.typeck_results(), + unsafe_ops: vec![], + }; + collector.visit_block(block); + collector.unsafe_ops + } +} + +impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { // The `await` itself will desugar to two unsafe calls, but we should ignore those. // Instead, check the expression that is `await`ed _ if let Some(e) = desugar_await(expr) => { - collect_unsafe_exprs(cx, e, unsafe_ops); - return Continue(Descend::No); + return self.visit_expr(e); }, - ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), + ExprKind::InlineAsm(_) => self.unsafe_ops.push(("inline assembly used here", expr.span)), + + ExprKind::AddrOf(BorrowKind::Raw, _, mut inner) => { + while let ExprKind::Field(prefix, _) = inner.kind + && self.typeck_results.expr_adjustments(prefix).is_empty() + { + inner = prefix; + } + return self.visit_expr(inner); + }, ExprKind::Field(e, _) => { - if cx.typeck_results().expr_ty(e).is_union() { - unsafe_ops.push(("union field access occurs here", expr.span)); + if self.typeck_results.expr_ty(e).is_union() { + self.unsafe_ops.push(("union field access occurs here", expr.span)); } }, @@ -127,32 +157,32 @@ fn collect_unsafe_exprs<'tcx>( .. }, )) => { - unsafe_ops.push(("access of a mutable static occurs here", expr.span)); + self.unsafe_ops + .push(("access of a mutable static occurs here", expr.span)); }, - ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_raw_ptr() => { - unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); + ExprKind::Unary(UnOp::Deref, e) if self.typeck_results.expr_ty(e).is_raw_ptr() => { + self.unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); }, ExprKind::Call(path_expr, _) => { - let sig = match *cx.typeck_results().expr_ty(path_expr).kind() { - ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(), - ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), - _ => return Continue(Descend::Yes), + let opt_sig = match *self.typeck_results.expr_ty_adjusted(path_expr).kind() { + ty::FnDef(id, _) => Some(self.tcx.fn_sig(id).skip_binder()), + ty::FnPtr(sig_tys, hdr) => Some(sig_tys.with(hdr)), + _ => None, }; - if sig.safety().is_unsafe() { - unsafe_ops.push(("unsafe function call occurs here", expr.span)); + if opt_sig.is_some_and(|sig| sig.safety().is_unsafe()) { + self.unsafe_ops.push(("unsafe function call occurs here", expr.span)); } }, ExprKind::MethodCall(..) => { - if let Some(sig) = cx - .typeck_results() + let opt_sig = self + .typeck_results .type_dependent_def_id(expr.hir_id) - .map(|def_id| cx.tcx.fn_sig(def_id)) - && sig.skip_binder().safety().is_unsafe() - { - unsafe_ops.push(("unsafe method call occurs here", expr.span)); + .map(|def_id| self.tcx.fn_sig(def_id)); + if opt_sig.is_some_and(|sig| sig.skip_binder().safety().is_unsafe()) { + self.unsafe_ops.push(("unsafe method call occurs here", expr.span)); } }, @@ -173,15 +203,26 @@ fn collect_unsafe_exprs<'tcx>( } )) ) { - unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); - collect_unsafe_exprs(cx, rhs, unsafe_ops); - return Continue(Descend::No); + self.unsafe_ops + .push(("modification of a mutable static occurs here", expr.span)); + return self.visit_expr(rhs); } }, _ => {}, } - Continue::<(), _>(Descend::Yes) - }); + walk_expr(self, expr); + } + + fn visit_body(&mut self, body: &hir::Body<'tcx>) { + let saved_typeck_results = self.typeck_results; + self.typeck_results = self.tcx.typeck_body(body.id()); + walk_body(self, body); + self.typeck_results = saved_typeck_results; + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } } diff --git a/clippy_lints/src/ptr/ptr_arg.rs b/clippy_lints/src/ptr/ptr_arg.rs index fd9230f00a8b..4bfff64b1bd4 100644 --- a/clippy_lints/src/ptr/ptr_arg.rs +++ b/clippy_lints/src/ptr/ptr_arg.rs @@ -2,7 +2,7 @@ use super::PTR_ARG; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, sym}; +use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::Applicability; @@ -23,8 +23,6 @@ use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::{fmt, iter}; -use crate::vec::is_allowed_vec_method; - pub(super) fn check_body<'tcx>( cx: &LateContext<'tcx>, body: &Body<'tcx>, @@ -383,7 +381,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type // doesn't coerce to a slice and our adjusted type check below isn't enough, // but it would still be valid to call with a slice - if is_allowed_vec_method(use_expr) { + if VEC_METHODS_SHADOWING_SLICE_METHODS.contains(&name) { return; } } diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index dbd4ec77fd5f..e0fdca97dbee 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; @@ -109,36 +109,16 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { - // For chained subtraction like (instant - dur1) - dur2, avoid suggestions - if is_chained_time_subtraction(cx, lhs) { - span_lint( - cx, - UNCHECKED_TIME_SUBTRACTION, - expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", - ); - } else { - // instant - duration - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); - } + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } - } else if lhs_ty.is_diag_item(cx, sym::Duration) + } + // duration - duration + else if lhs_ty.is_diag_item(cx, sym::Duration) && rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { - // For chained subtraction like (dur1 - dur2) - dur3, avoid suggestions - if is_chained_time_subtraction(cx, lhs) { - span_lint( - cx, - UNCHECKED_TIME_SUBTRACTION, - expr.span, - "unchecked subtraction between 'Duration' values", - ); - } else { - // duration - duration - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); - } + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } } @@ -191,26 +171,26 @@ fn print_unchecked_duration_subtraction_sugg( right_expr: &Expr<'_>, expr: &Expr<'_>, ) { - let typeck = cx.typeck_results(); - let left_ty = typeck.expr_ty(left_expr); - - let lint_msg = if left_ty.is_diag_item(cx, sym::Instant) { - "unchecked subtraction of a 'Duration' from an 'Instant'" - } else { - "unchecked subtraction between 'Duration' values" - }; - - let mut applicability = Applicability::MachineApplicable; - let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); - let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); - - span_lint_and_sugg( + span_lint_and_then( cx, UNCHECKED_TIME_SUBTRACTION, expr.span, - lint_msg, - "try", - format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), - applicability, + "unchecked subtraction of a `Duration`", + |diag| { + // For chained subtraction, like `(dur1 - dur2) - dur3` or `(instant - dur1) - dur2`, + // avoid suggestions + if !is_chained_time_subtraction(cx, left_expr) { + let mut applicability = Applicability::MachineApplicable; + let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); + let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); + + diag.span_suggestion( + expr.span, + "try", + format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), + applicability, + ); + } + }, ); } diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 933e25fe98c6..91fce5d5bd68 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -17,6 +17,8 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, msrv: Msrv, ) -> bool { + let mut applicability = Applicability::MachineApplicable; + let arg_sugg = sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut applicability); match (from_ty.kind(), to_ty.kind()) { (ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => { span_lint_and_then( @@ -25,40 +27,38 @@ pub(super) fn check<'tcx>( e.span, "transmute from a pointer to a pointer", |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - if from_mutbl == to_mutbl - && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) - && msrv.meets(cx, msrvs::POINTER_CAST) - { - diag.span_suggestion_verbose( - e.span, - "use `pointer::cast` instead", - format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()), - Applicability::MaybeIncorrect, - ); - } else if from_pointee_ty == to_pointee_ty - && let Some(method) = match (from_mutbl, to_mutbl) { - (ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"), - (ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"), - _ => None, - } - && !from_pointee_ty.has_erased_regions() - && msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) - { - diag.span_suggestion_verbose( - e.span, - format!("use `pointer::{method}` instead"), - format!("{}.{method}()", arg.maybe_paren()), - Applicability::MaybeIncorrect, - ); - } else { - diag.span_suggestion_verbose( - e.span, - "use an `as` cast instead", - arg.as_ty(to_ty), - Applicability::MaybeIncorrect, - ); + if from_mutbl == to_mutbl + && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) + && msrv.meets(cx, msrvs::POINTER_CAST) + { + diag.span_suggestion_verbose( + e.span, + "use `pointer::cast` instead", + format!("{}.cast::<{to_pointee_ty}>()", arg_sugg.maybe_paren()), + Applicability::MaybeIncorrect, + ); + } else if from_pointee_ty == to_pointee_ty + && let Some(method) = match (from_mutbl, to_mutbl) { + (ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"), + (ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"), + _ => None, } + && !from_pointee_ty.has_erased_regions() + && msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) + { + diag.span_suggestion_verbose( + e.span, + format!("use `pointer::{method}` instead"), + format!("{}.{method}()", arg_sugg.maybe_paren()), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion_verbose( + e.span, + "use an `as` cast instead", + arg_sugg.as_ty(to_ty), + Applicability::MaybeIncorrect, + ); } }, ); diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 70c2a73ce6ef..39b1ccdc9426 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,6 +1,5 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet; use clippy_utils::{std_or_core, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; @@ -17,8 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, const_context: bool, ) -> bool { - let mut triggered = false; - + let arg_sugg = || sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut Applicability::Unspecified); if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (*from_ty.kind(), *to_ty.kind()) { if let ty::Slice(slice_ty) = *ty_from.kind() && ty_to.is_str() @@ -29,8 +27,6 @@ pub(super) fn check<'tcx>( let postfix = if from_mutbl == Mutability::Mut { "_mut" } else { "" }; - let snippet = snippet(cx, arg.span, ".."); - span_lint_and_sugg( cx, TRANSMUTE_BYTES_TO_STR, @@ -38,15 +34,17 @@ pub(super) fn check<'tcx>( format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { - format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})") + format!("{top_crate}::str::from_utf8_unchecked{postfix}({})", arg_sugg()) } else { - format!("{top_crate}::str::from_utf8{postfix}({snippet}).unwrap()") + format!("{top_crate}::str::from_utf8{postfix}({}).unwrap()", arg_sugg()) }, Applicability::MaybeIncorrect, ); - triggered = true; - } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) - && !const_context + + return true; + } + + if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { span_lint_and_then( cx, @@ -54,23 +52,21 @@ pub(super) fn check<'tcx>( e.span, "transmute from a reference to a reference", |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg_paren = arg - .as_ty(Ty::new_ptr(cx.tcx, ty_from, from_mutbl)) - .as_ty(Ty::new_ptr(cx.tcx, ty_to, to_mutbl)); - let sugg = if to_mutbl == Mutability::Mut { - sugg_paren.mut_addr_deref() - } else { - sugg_paren.addr_deref() - }; - diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); - } + let sugg_paren = arg_sugg() + .as_ty(Ty::new_ptr(cx.tcx, ty_from, from_mutbl)) + .as_ty(Ty::new_ptr(cx.tcx, ty_to, to_mutbl)); + let sugg = if to_mutbl == Mutability::Mut { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); }, ); - triggered = true; + return true; } } - triggered + false } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 99201a1ca215..8ed3df8731b3 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,15 +1,21 @@ +use std::borrow::Cow; +use std::iter; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::source::snippet; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{can_use_if_let_chains, higher, sym}; +use rustc_abi::FieldIdx; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; -use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceWithHirId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; +use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; @@ -114,11 +120,91 @@ impl UnwrappableKind { } } +#[derive(Clone, Debug, Eq)] +enum Local { + /// `x.field1.field2.field3` + WithFieldAccess { + local_id: HirId, + /// The indices of the field accessed. + /// + /// Stored last-to-first, e.g. for the example above: `[field3, field2, field1]` + field_indices: Vec, + /// The span of the whole expression + span: Span, + }, + /// `x` + Pure { local_id: HirId }, +} + +/// Identical to derived impl, but ignores `span` on [`Local::WithFieldAccess`] +impl PartialEq for Local { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + Self::WithFieldAccess { + local_id: self_local_id, + field_indices: self_field_indices, + .. + }, + Self::WithFieldAccess { + local_id: other_local_id, + field_indices: other_field_indices, + .. + }, + ) => self_local_id == other_local_id && self_field_indices == other_field_indices, + ( + Self::Pure { + local_id: self_local_id, + }, + Self::Pure { + local_id: other_local_id, + }, + ) => self_local_id == other_local_id, + _ => false, + } + } +} + +impl Local { + fn snippet(&self, cx: &LateContext<'_>) -> Cow<'static, str> { + match *self { + Self::WithFieldAccess { span, .. } => snippet(cx.sess(), span, "_"), + Self::Pure { local_id } => cx.tcx.hir_name(local_id).to_string().into(), + } + } + + fn is_potentially_local_place(&self, place: &Place<'_>) -> bool { + match self { + Self::WithFieldAccess { + local_id, + field_indices, + .. + } => { + is_potentially_local_place(*local_id, place) + // If there were projections other than field projections, err on the side of caution and say that they + // _might_ be mutating something. + // + // The reason we use `<=` and not `==` is that a mutation of `struct` or `struct.field1` should count as + // mutation of the child fields such as `struct.field1.field2` + && place.projections.len() <= field_indices.len() + && iter::zip(&place.projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| { + match proj.kind { + ProjectionKind::Field(f_idx, _) => f_idx == field_idx, + // If this is a projection we don't expect, it _might_ be mutating something + _ => false, + } + }) + }, + Self::Pure { local_id } => is_potentially_local_place(*local_id, place), + } + } +} + /// Contains information about whether a variable can be unwrapped. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] struct UnwrapInfo<'tcx> { /// The variable that is checked - local_id: HirId, + local: Local, /// The if itself if_expr: &'tcx Expr<'tcx>, /// The check, like `x.is_ok()` @@ -156,38 +242,77 @@ fn collect_unwrap_info<'tcx>( } } - match expr.kind { - ExprKind::Binary(op, left, right) - if matches!( - (invert, op.node), - (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) - ) => - { - let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false); - unwrap_info.extend(collect_unwrap_info(cx, if_expr, right, branch, invert, false)); - unwrap_info - }, - ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false), - ExprKind::MethodCall(method_name, receiver, [], _) - if let Some(local_id) = receiver.res_local_id() - && let ty = cx.typeck_results().expr_ty(receiver) - && let name = method_name.ident.name - && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => - { - let safe_to_unwrap = unwrappable != invert; + fn inner<'tcx>( + cx: &LateContext<'tcx>, + if_expr: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, + invert: bool, + is_entire_condition: bool, + out: &mut Vec>, + ) { + match expr.kind { + ExprKind::Binary(op, left, right) + if matches!( + (invert, op.node), + (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) + ) => + { + inner(cx, if_expr, left, branch, invert, false, out); + inner(cx, if_expr, right, branch, invert, false, out); + }, + ExprKind::Unary(UnOp::Not, expr) => inner(cx, if_expr, expr, branch, !invert, false, out), + ExprKind::MethodCall(method_name, receiver, [], _) + if let Some(local) = extract_local(cx, receiver) + && let ty = cx.typeck_results().expr_ty(receiver) + && let name = method_name.ident.name + && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => + { + let safe_to_unwrap = unwrappable != invert; - vec![UnwrapInfo { + out.push(UnwrapInfo { + local, + if_expr, + check: expr, + check_name: name, + branch, + safe_to_unwrap, + kind, + is_entire_condition, + }); + }, + _ => {}, + } + } + + let mut out = vec![]; + inner(cx, if_expr, expr, branch, invert, is_entire_condition, &mut out); + out +} + +/// Extracts either a local used by itself ([`Local::Pure`]), or (one or more levels of) field +/// access to a local ([`Local::WithFieldAccess`]) +fn extract_local(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> Option { + let span = expr.span; + let mut field_indices = vec![]; + while let ExprKind::Field(recv, _) = expr.kind + && let Some(field_idx) = cx.typeck_results().opt_field_index(expr.hir_id) + { + field_indices.push(field_idx); + expr = recv; + } + if let Some(local_id) = expr.res_local_id() { + if field_indices.is_empty() { + Some(Local::Pure { local_id }) + } else { + Some(Local::WithFieldAccess { local_id, - if_expr, - check: expr, - check_name: name, - branch, - safe_to_unwrap, - kind, - is_entire_condition, - }] - }, - _ => vec![], + field_indices, + span, + }) + } + } else { + None } } @@ -198,9 +323,9 @@ fn collect_unwrap_info<'tcx>( /// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if /// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`. /// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) -struct MutationVisitor<'tcx> { +struct MutationVisitor<'tcx, 'lcl> { is_mutated: bool, - local_id: HirId, + local: &'lcl Local, tcx: TyCtxt<'tcx>, } @@ -221,10 +346,10 @@ fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { } } -impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { +impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx, '_> { fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::Mutable = bk - && is_potentially_local_place(self.local_id, &cat.place) + && self.local.is_potentially_local_place(&cat.place) && !is_as_mut_use(self.tcx, diag_expr_id) { self.is_mutated = true; @@ -232,7 +357,7 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { } fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) { - if is_potentially_local_place(self.local_id, &cat.place) { + if self.local.is_potentially_local_place(&cat.place) { self.is_mutated = true; } } @@ -256,7 +381,7 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { is_mutated: false, - local_id: unwrap_info.local_id, + local: &unwrap_info.local, tcx: self.cx.tcx, }; @@ -318,24 +443,17 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { // find `unwrap[_err]()` or `expect("...")` calls: if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) - && let Some(id) = self_arg.res_local_id() + && let Some(local) = extract_local(self.cx, self_arg) && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) - && let Some(unwrappable) = self.unwrappables.iter() - .find(|u| u.local_id == id) + && let Some(unwrappable) = self.unwrappables.iter().find(|u| u.local == local) // Span contexts should not differ with the conditional branch && let span_ctxt = expr.span.ctxt() && unwrappable.branch.span.ctxt() == span_ctxt && unwrappable.check.span.ctxt() == span_ctxt { if call_to_unwrap == unwrappable.safe_to_unwrap { - let is_entire_condition = unwrappable.is_entire_condition; - let unwrappable_variable_name = self.cx.tcx.hir_name(unwrappable.local_id); - let suggested_pattern = if call_to_unwrap { - unwrappable.kind.success_variant_pattern() - } else { - unwrappable.kind.error_variant_pattern() - }; + let unwrappable_variable_str = unwrappable.local.snippet(self.cx); span_lint_hir_and_then( self.cx, @@ -343,16 +461,21 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { expr.hir_id, expr.span, format!( - "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", + "called `{}` on `{unwrappable_variable_str}` after checking its variant with `{}`", method_name.ident.name, unwrappable.check_name, ), |diag| { - if is_entire_condition { + if unwrappable.is_entire_condition { diag.span_suggestion( unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", + "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_str}", + suggested_pattern = if call_to_unwrap { + unwrappable.kind.success_variant_pattern() + } else { + unwrappable.kind.error_variant_pattern() + }, borrow_prefix = match as_ref_kind { Some(AsRefKind::AsRef) => "&", Some(AsRefKind::AsMut) => "&mut ", diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/useless_vec.rs similarity index 90% rename from clippy_lints/src/vec.rs rename to clippy_lints/src/useless_vec.rs index b87db836869d..28c339ce2b7d 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/useless_vec.rs @@ -10,7 +10,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, span_contains_comment, sym}; +use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_parent_expr, higher, is_in_test, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -123,8 +123,16 @@ impl UselessVec { // allow indexing into a vec and some set of allowed method calls that exist on slices, too if let Some(parent) = get_parent_expr(cx, expr) && (adjusts_to_slice(cx, expr) - || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(parent)) + || match parent.kind { + ExprKind::Index(..) => true, + ExprKind::MethodCall(path, _, [], _) => { + // If the given expression is a method call to a `Vec` method that also exists on + // slices, it means that this expression does not actually require a `Vec` and could + // just work with an array. + VEC_METHODS_SHADOWING_SLICE_METHODS.contains(&path.ident.name) + }, + _ => false, + }) { ControlFlow::Continue(()) } else { @@ -144,8 +152,9 @@ impl UselessVec { VecToArray::Impossible }, // search for `for _ in vec![...]` - Node::Expr(Expr { span, .. }) - if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => + Node::Expr(expr) + if expr.span.is_desugaring(DesugaringKind::ForLoop) + && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => { VecToArray::Possible }, @@ -276,9 +285,8 @@ impl SuggestedType { assert!(args_span.is_none_or(|s| !s.from_expansion())); assert!(len_span.is_none_or(|s| !s.from_expansion())); - let maybe_args = args_span - .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) - .map_or(String::new(), |x| x.to_owned()); + let maybe_args = args_span.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")); + let maybe_args = maybe_args.as_deref().unwrap_or_default(); let maybe_len = len_span .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) .map(|st| format!("; {st}")) @@ -301,17 +309,6 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { matches!(cx.typeck_results().expr_ty_adjusted(e).kind(), ty::Ref(_, ty, _) if ty.is_slice()) } -/// Checks if the given expression is a method call to a `Vec` method -/// that also exists on slices. If this returns true, it means that -/// this expression does not actually require a `Vec` and could just work with an array. -pub fn is_allowed_vec_method(e: &Expr<'_>) -> bool { - if let ExprKind::MethodCall(path, _, [], _) = e.kind { - matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) - } else { - false - } -} - fn suggest_type(expr: &Expr<'_>) -> SuggestedType { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 6f976094fc2d..4b1a10a3d9cf 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-11-14 +nightly-2025-11-28 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 4bdbfc885366..27b5a57c737d 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -43,7 +43,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Range(lf, lt, le), Range(rf, rt, re)) => { eq_expr_opt(lf.as_deref(), rf.as_deref()) && eq_expr_opt(lt.as_deref(), rt.as_deref()) - && eq_range_end(&le.node, &re.node) + && eq_range_end(le.node, re.node) }, (Box(l), Box(r)) => eq_pat(l, r), (Ref(l, l_pin, l_mut), Ref(r, r_pin, r_mut)) => l_pin == r_pin && l_mut == r_mut && eq_pat(l, r), @@ -64,7 +64,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { } } -pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { +pub fn eq_range_end(l: RangeEnd, r: RangeEnd) -> bool { match (l, r) { (RangeEnd::Excluded, RangeEnd::Excluded) => true, (RangeEnd::Included(l), RangeEnd::Included(r)) => { diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 50d9136b8b4d..d9254fca9453 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -265,7 +265,14 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")), ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")), ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")), - _ => return (Pat::Str(""), Pat::Str("")), + ItemKind::Mod(..) => (Pat::Str("mod"), Pat::Str("")), + ItemKind::Macro(_, def, _) => ( + Pat::Str(if def.macro_rules { "macro_rules" } else { "macro" }), + Pat::Str(""), + ), + ItemKind::TraitAlias(..) => (Pat::Str("trait"), Pat::Str(";")), + ItemKind::GlobalAsm { .. } => return (Pat::Str("global_asm"), Pat::Str("")), + ItemKind::Use(..) => return (Pat::Str(""), Pat::Str("")), }; if item.vis_span.is_empty() { (start_pat, end_pat) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c9302b17eb7e..ed164fcf371b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -136,6 +136,9 @@ use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; +/// Methods on `Vec` that also exists on slices. +pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len]; + #[macro_export] macro_rules! extract_msrv_attr { () => { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 2593df103527..2ef2afb45071 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -8,7 +8,7 @@ use rustc_ast::util::parser::AssocOp; use rustc_ast::{UnOp, ast}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Closure, ExprKind, HirId, MutTy, Node, TyKind}; +use rustc_hir::{self as hir, Closure, ExprKind, HirId, MatchSource, MutTy, Node, TyKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -146,7 +146,9 @@ impl<'a> Sugg<'a> { | ExprKind::Let(..) | ExprKind::Closure { .. } | ExprKind::Unary(..) - | ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)), + | ExprKind::Match(_, _, + MatchSource::Normal | MatchSource::Postfix | MatchSource::ForLoopDesugar + ) => Sugg::MaybeParen(get_snippet(expr.span)), ExprKind::Continue(..) | ExprKind::Yield(..) | ExprKind::Array(..) @@ -169,7 +171,10 @@ impl<'a> Sugg<'a> { | ExprKind::Tup(..) | ExprKind::Use(..) | ExprKind::Err(_) - | ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)), + | ExprKind::UnsafeBinderCast(..) + | ExprKind::Match(_, _, + MatchSource::AwaitDesugar | MatchSource::TryDesugar(_) | MatchSource::FormatArgs + ) => Sugg::NonParen(get_snippet(expr.span)), ExprKind::DropTemps(inner) => Self::hir_from_snippet(cx, inner, get_snippet), ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f6809da98f2b..5157b79832a3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-11-14" +channel = "nightly-2025-11-28" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index 2fc4abe48fe4..8693973ef78c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -13,12 +13,12 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; -/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs -/// and https://github.com/rust-lang/rust/pull/146627 for why we need this. +/// See docs in +/// and for why we need this. /// /// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates /// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to -/// https://github.com/rust-lang/cc-rs/issues/1613. +/// . #[cfg(feature = "jemalloc")] extern crate tikv_jemalloc_sys as _; @@ -191,7 +191,6 @@ fn display_help() { const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -#[expect(clippy::too_many_lines)] pub fn main() { let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs deleted file mode 100644 index 155f680c7b13..000000000000 --- a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Test file for missing_docs_in_private_items lint with allow_unused configuration -#![warn(clippy::missing_docs_in_private_items)] -#![allow(dead_code)] - -/// A struct with some documented and undocumented fields -struct Test { - /// This field is documented - field1: i32, - _unused: i32, // This should not trigger a warning because it starts with an underscore - field3: i32, //~ missing_docs_in_private_items -} - -struct Test2 { - //~^ missing_docs_in_private_items - _field1: i32, // This should not trigger a warning - _field2: i32, // This should not trigger a warning -} - -struct Test3 { - //~^ missing_docs_in_private_items - /// This field is documented although this is not mandatory - _unused: i32, // This should not trigger a warning because it starts with an underscore - field2: i32, //~ missing_docs_in_private_items -} - -fn main() {} diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr deleted file mode 100644 index 8f511883e900..000000000000 --- a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: missing documentation for a struct field - --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:10:5 - | -LL | field3: i32, - | ^^^^^^^^^^^ - | - = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` - -error: missing documentation for a struct - --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:13:1 - | -LL | / struct Test2 { -LL | | -LL | | _field1: i32, // This should not trigger a warning -LL | | _field2: i32, // This should not trigger a warning -LL | | } - | |_^ - -error: missing documentation for a struct - --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:19:1 - | -LL | / struct Test3 { -LL | | -LL | | /// This field is documented although this is not mandatory -LL | | _unused: i32, // This should not trigger a warning because it starts with an underscore -LL | | field2: i32, -LL | | } - | |_^ - -error: missing documentation for a struct field - --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:23:5 - | -LL | field2: i32, - | ^^^^^^^^^^^ - -error: aborting due to 4 previous errors - diff --git a/tests/ui-toml/missing_docs_allow_unused/clippy.toml b/tests/ui-toml/missing_docs_in_private_items/allow_unused/clippy.toml similarity index 100% rename from tests/ui-toml/missing_docs_allow_unused/clippy.toml rename to tests/ui-toml/missing_docs_in_private_items/allow_unused/clippy.toml diff --git a/tests/ui-toml/pub_crate_missing_docs/clippy.toml b/tests/ui-toml/missing_docs_in_private_items/crate_root/clippy.toml similarity index 100% rename from tests/ui-toml/pub_crate_missing_docs/clippy.toml rename to tests/ui-toml/missing_docs_in_private_items/crate_root/clippy.toml diff --git a/tests/ui-toml/missing_docs_in_private_items/default/clippy.toml b/tests/ui-toml/missing_docs_in_private_items/default/clippy.toml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.allow_unused.stderr b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.allow_unused.stderr new file mode 100644 index 000000000000..b686288c7d27 --- /dev/null +++ b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.allow_unused.stderr @@ -0,0 +1,644 @@ +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:26:5 + | +LL | f3: u32, + | ^^ + | +note: the lint level is defined here + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:8:9 + | +LL | #![deny(clippy::missing_docs_in_private_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:60:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:77:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:78:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:117:4 + | +LL | fn fn_crate() {} + | ^^^^^^^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:118:7 + | +LL | const CONST_CRATE: u32 = 0; + | ^^^^^^^^^^^ + +error: missing documentation for a static + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:119:8 + | +LL | static STATIC_CRATE: u32 = 0; + | ^^^^^^^^^^^^ + +error: missing documentation for a type alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:120:6 + | +LL | type TyAliasCrate = u32; + | ^^^^^^^^^^^^ + +error: missing documentation for a trait alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:121:7 + | +LL | trait TraitAliasCrate = Iterator; + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:122:8 + | +LL | struct StructCrate; + | ^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:124:8 + | +LL | struct StructFieldCrate { + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:125:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:128:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:138:8 + | +LL | struct StructTupleCrate(u32, pub u32); + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:140:6 + | +LL | enum EnumCrate { + | ^^^^^^^^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:141:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:142:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:144:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:145:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:155:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a union + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:161:7 + | +LL | union UnionCrate { + | ^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:162:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:165:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:176:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:177:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:182:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:183:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a trait + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:190:7 + | +LL | trait TraitCrate { + | ^^^^^^^^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:191:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:192:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:193:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:194:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:195:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:216:14 + | +LL | macro_rules! mac_rules_crate { + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:220:7 + | +LL | macro mac_crate { + | ^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:238:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:241:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:255:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:256:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:258:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:259:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:269:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:276:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:279:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:290:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:291:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:296:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:297:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:305:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:306:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:307:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:308:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:309:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:541:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:567:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:588:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:591:12 + | +LL | struct S3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:592:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:595:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:600:10 + | +LL | enum E3 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:602:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:603:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:609:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:614:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:620:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:623:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:630:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:631:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:637:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a module + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:647:5 + | +LL | mod mod_crate { + | ^^^^^^^^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:648:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:651:16 + | +LL | pub struct S1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:652:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:655:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:660:14 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:662:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:663:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:669:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:674:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:680:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:683:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:690:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:691:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:697:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:705:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:708:12 + | +LL | struct S3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:709:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:712:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:717:10 + | +LL | enum E3 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:719:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:720:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:726:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:731:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:737:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:740:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:747:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:748:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:754:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1061:9 + | +LL | f2: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1062:20 + | +LL | pub(crate) f3: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1069:23 + | +LL | pub(crate) fn f2() {} + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1072:18 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1074:13 + | +LL | V2, + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1077:20 + | +LL | pub struct S2; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1096:18 + | +LL | $(pub fn f2() {}) + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1165:20 + | +LL | pub struct VisFromOutside; + | ^^^^^^^^^^^^^^ + +error: aborting due to 106 previous errors + diff --git a/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.crate_root.stderr b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.crate_root.stderr new file mode 100644 index 000000000000..e95ce5615cce --- /dev/null +++ b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.crate_root.stderr @@ -0,0 +1,500 @@ +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:26:5 + | +LL | f3: u32, + | ^^ + | +note: the lint level is defined here + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:8:9 + | +LL | #![deny(clippy::missing_docs_in_private_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:32:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:60:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:66:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:77:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:78:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:117:4 + | +LL | fn fn_crate() {} + | ^^^^^^^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:118:7 + | +LL | const CONST_CRATE: u32 = 0; + | ^^^^^^^^^^^ + +error: missing documentation for a static + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:119:8 + | +LL | static STATIC_CRATE: u32 = 0; + | ^^^^^^^^^^^^ + +error: missing documentation for a type alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:120:6 + | +LL | type TyAliasCrate = u32; + | ^^^^^^^^^^^^ + +error: missing documentation for a trait alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:121:7 + | +LL | trait TraitAliasCrate = Iterator; + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:122:8 + | +LL | struct StructCrate; + | ^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:124:8 + | +LL | struct StructFieldCrate { + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:125:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:128:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:131:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:134:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:138:8 + | +LL | struct StructTupleCrate(u32, pub u32); + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:140:6 + | +LL | enum EnumCrate { + | ^^^^^^^^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:141:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:142:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:144:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:145:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:155:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a union + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:161:7 + | +LL | union UnionCrate { + | ^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:162:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:165:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:168:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:171:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:176:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:177:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:182:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:183:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a trait + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:190:7 + | +LL | trait TraitCrate { + | ^^^^^^^^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:191:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:192:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:193:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:194:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:195:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:216:14 + | +LL | macro_rules! mac_rules_crate { + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:220:7 + | +LL | macro mac_crate { + | ^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:238:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:241:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:244:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:247:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:255:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:256:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:258:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:259:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:269:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:276:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:279:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:282:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:285:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:290:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:291:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:296:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:297:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:305:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:306:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:307:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:308:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:309:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a module + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:647:5 + | +LL | mod mod_crate { + | ^^^^^^^^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:648:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:651:16 + | +LL | pub struct S1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:652:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:660:14 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:662:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:663:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:669:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:674:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:680:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:690:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:691:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:697:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1062:20 + | +LL | pub(crate) f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1072:18 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1074:13 + | +LL | V2, + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1077:20 + | +LL | pub struct S2; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1096:18 + | +LL | $(pub fn f2() {}) + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1165:20 + | +LL | pub struct VisFromOutside; + | ^^^^^^^^^^^^^^ + +error: aborting due to 82 previous errors + diff --git a/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.default.stderr b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.default.stderr new file mode 100644 index 000000000000..44a3d29580ad --- /dev/null +++ b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.default.stderr @@ -0,0 +1,704 @@ +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:26:5 + | +LL | f3: u32, + | ^^ + | +note: the lint level is defined here + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:8:9 + | +LL | #![deny(clippy::missing_docs_in_private_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:32:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:60:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:66:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:77:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:78:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:117:4 + | +LL | fn fn_crate() {} + | ^^^^^^^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:118:7 + | +LL | const CONST_CRATE: u32 = 0; + | ^^^^^^^^^^^ + +error: missing documentation for a static + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:119:8 + | +LL | static STATIC_CRATE: u32 = 0; + | ^^^^^^^^^^^^ + +error: missing documentation for a type alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:120:6 + | +LL | type TyAliasCrate = u32; + | ^^^^^^^^^^^^ + +error: missing documentation for a trait alias + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:121:7 + | +LL | trait TraitAliasCrate = Iterator; + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:122:8 + | +LL | struct StructCrate; + | ^^^^^^^^^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:124:8 + | +LL | struct StructFieldCrate { + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:125:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:128:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:131:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:134:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:138:8 + | +LL | struct StructTupleCrate(u32, pub u32); + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:140:6 + | +LL | enum EnumCrate { + | ^^^^^^^^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:141:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:142:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:144:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:145:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:155:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a union + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:161:7 + | +LL | union UnionCrate { + | ^^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:162:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:165:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:168:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:171:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:176:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:177:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:182:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:183:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a trait + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:190:7 + | +LL | trait TraitCrate { + | ^^^^^^^^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:191:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:192:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:193:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:194:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:195:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:216:14 + | +LL | macro_rules! mac_rules_crate { + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a macro + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:220:7 + | +LL | macro mac_crate { + | ^^^^^^^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:238:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:241:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:244:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:247:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:255:5 + | +LL | V1, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:256:5 + | +LL | V2(u32), + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:258:5 + | +LL | V3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:259:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:269:9 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:276:9 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:279:5 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:282:9 + | +LL | pub _f5: u32, + | ^^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:285:5 + | +LL | _f7: u32, + | ^^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:290:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:291:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:296:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:297:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:305:8 + | +LL | fn f1(); + | ^^ + +error: missing documentation for an associated function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:306:8 + | +LL | fn f2() {} + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:307:11 + | +LL | const C1: u32; + | ^^ + +error: missing documentation for an associated constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:308:11 + | +LL | const C2: u32 = 0; + | ^^ + +error: missing documentation for an associated type + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:309:10 + | +LL | type T1; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:541:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:567:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:588:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:591:12 + | +LL | struct S3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:592:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:595:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:600:10 + | +LL | enum E3 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:602:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:603:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:609:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:614:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:620:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:623:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:630:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:631:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:637:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a module + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:647:5 + | +LL | mod mod_crate { + | ^^^^^^^^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:648:12 + | +LL | pub fn f1() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:651:16 + | +LL | pub struct S1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:652:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:655:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:660:14 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:662:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:663:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:669:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:674:15 + | +LL | pub const C1: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:680:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:683:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:690:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:691:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:697:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:705:8 + | +LL | fn f3() {} + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:708:12 + | +LL | struct S3 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:709:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:712:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:717:10 + | +LL | enum E3 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:719:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:720:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:726:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a constant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:731:11 + | +LL | const C3: u32 = 0; + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:737:13 + | +LL | pub f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:740:9 + | +LL | f3: u32, + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:747:9 + | +LL | V1 { + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:748:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:754:13 + | +LL | f1: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1061:9 + | +LL | f2: u32, + | ^^ + +error: missing documentation for a field + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1062:20 + | +LL | pub(crate) f3: u32, + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1069:23 + | +LL | pub(crate) fn f2() {} + | ^^ + +error: missing documentation for an enum + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1072:18 + | +LL | pub enum E1 { + | ^^ + +error: missing documentation for a variant + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1074:13 + | +LL | V2, + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1077:20 + | +LL | pub struct S2; + | ^^ + +error: missing documentation for a function + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1096:18 + | +LL | $(pub fn f2() {}) + | ^^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs:1165:20 + | +LL | pub struct VisFromOutside; + | ^^^^^^^^^^^^^^ + +error: aborting due to 116 previous errors + diff --git a/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs new file mode 100644 index 000000000000..0a7730a4ee7f --- /dev/null +++ b/tests/ui-toml/missing_docs_in_private_items/missing_docs_in_private_items.rs @@ -0,0 +1,1167 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default crate_root allow_unused +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/missing_docs_in_private_items/default +//@[crate_root] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/missing_docs_in_private_items/crate_root +//@[allow_unused] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/missing_docs_in_private_items/allow_unused + +#![feature(decl_macro, trait_alias)] +#![deny(clippy::missing_docs_in_private_items)] +#![allow(non_local_definitions)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +fn main() {} + +pub fn fn_pub() {} +pub const CONST_PUB: u32 = 0; +pub static STATIC_PUB: u32 = 0; +pub type TyAliasPub = u32; +pub trait TraitAliasPub = Iterator; +pub struct StructPub; +pub struct StructFieldPub { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +pub struct StructTuplePub(u32, pub u32); +pub enum EnumPub { + V1, + V2(u32), + V3 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V4, + /// docs + V5(u32), + /// docs + V6 { + f1: u32, + /// docs + f2: u32, + }, +} +pub union UnionPub { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +impl StructFieldPub { + pub fn f1() {} + pub const C1: u32 = 0; + /// docs + pub fn f2() {} + /// docs + pub const C2: u32 = 0; + fn f3() {} //~ missing_docs_in_private_items + const C3: u32 = 0; //~ missing_docs_in_private_items + /// docs + fn f4() {} + /// docs + const C4: u32 = 0; +} +pub trait TraitPub { + fn f1(); + fn f2() {} + const C1: u32; + const C2: u32 = 0; + type T1; + /// docs + fn f3(); + /// docs + fn f4() {} + /// docs + const C3: u32; + /// docs + const C4: u32 = 0; + /// docs + type T2; +} +impl TraitPub for StructPub { + fn f1() {} + const C1: u32 = 0; + type T1 = u32; + fn f3() {} + const C3: u32 = 0; + type T2 = u32; +} +#[macro_export] +macro_rules! mac_rules_pub { + () => {}; +} +pub macro mac_pub { + () => {}, +} + +fn fn_crate() {} //~ missing_docs_in_private_items +const CONST_CRATE: u32 = 0; //~ missing_docs_in_private_items +static STATIC_CRATE: u32 = 0; //~ missing_docs_in_private_items +type TyAliasCrate = u32; //~ missing_docs_in_private_items +trait TraitAliasCrate = Iterator; //~ missing_docs_in_private_items +struct StructCrate; //~ missing_docs_in_private_items +//~v missing_docs_in_private_items +struct StructFieldCrate { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +struct StructTupleCrate(u32, pub u32); //~ missing_docs_in_private_items +//~v missing_docs_in_private_items +enum EnumCrate { + V1, //~ missing_docs_in_private_items + V2(u32), //~ missing_docs_in_private_items + //~v missing_docs_in_private_items + V3 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V4, + /// docs + V5(u32), + /// docs + V6 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, +} +//~v missing_docs_in_private_items +union UnionCrate { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +impl StructFieldCrate { + pub fn f1() {} //~ missing_docs_in_private_items + pub const C1: u32 = 0; //~ missing_docs_in_private_items + /// docs + pub fn f2() {} + /// docs + pub const C2: u32 = 0; + fn f3() {} //~ missing_docs_in_private_items + const C3: u32 = 0; //~ missing_docs_in_private_items + /// docs + fn f4() {} + /// docs + const C4: u32 = 0; +} +//~v missing_docs_in_private_items +trait TraitCrate { + fn f1(); //~ missing_docs_in_private_items + fn f2() {} //~ missing_docs_in_private_items + const C1: u32; //~ missing_docs_in_private_items + const C2: u32 = 0; //~ missing_docs_in_private_items + type T1; //~ missing_docs_in_private_items + /// docs + fn f3(); + /// docs + fn f4() {} + /// docs + const C3: u32; + /// docs + const C4: u32 = 0; + /// docs + type T2; +} +impl TraitCrate for StructCrate { + fn f1() {} + const C1: u32 = 0; + type T1 = u32; + fn f3() {} + const C3: u32 = 0; + type T2 = u32; +} +//~v missing_docs_in_private_items +macro_rules! mac_rules_crate { + () => {}; +} +//~v missing_docs_in_private_items +macro mac_crate { + () => {}, +} + +/// docs +fn fn_crate_doc() {} +/// docs +const CONST_CRATE_DOC: u32 = 0; +/// docs +static STATIC_CRATE_DOC: u32 = 0; +/// docs +type TyAliasCrateDoc = u32; +/// docs +trait TraitAliasCrateDoc = Iterator; +/// docs +struct StructCrateDoc; +/// docs +struct StructFieldCrateDoc { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +/// docs +struct StructTupleCrateDoc(u32, pub u32); +/// docs +enum EnumCrateDoc { + V1, //~ missing_docs_in_private_items + V2(u32), //~ missing_docs_in_private_items + //~v missing_docs_in_private_items + V3 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V4, + /// docs + V5(u32), + /// docs + V6 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, +} +/// docs +union UnionCrateDoc { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + pub _f5: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + pub _f6: u32, + _f7: u32, //~[default,crate_root] missing_docs_in_private_items + /// docs + _f8: u32, +} +impl StructFieldCrateDoc { + pub fn f1() {} //~ missing_docs_in_private_items + pub const C1: u32 = 0; //~ missing_docs_in_private_items + /// docs + pub fn f2() {} + /// docs + pub const C2: u32 = 0; + fn f3() {} //~ missing_docs_in_private_items + const C3: u32 = 0; //~ missing_docs_in_private_items + /// docs + fn f4() {} + /// docs + const C4: u32 = 0; +} +/// docs +trait TraitCrateDoc { + fn f1(); //~ missing_docs_in_private_items + fn f2() {} //~ missing_docs_in_private_items + const C1: u32; //~ missing_docs_in_private_items + const C2: u32 = 0; //~ missing_docs_in_private_items + type T1; //~ missing_docs_in_private_items + /// docs + fn f3(); + /// docs + fn f4() {} + /// docs + const C3: u32; + /// docs + const C4: u32 = 0; + /// docs + type T2; +} +impl TraitCrate for StructCrateDoc { + fn f1() {} + const C1: u32 = 0; + type T1 = u32; + fn f3() {} + const C3: u32 = 0; + type T2 = u32; +} +/// docs +macro_rules! mac_rules_crate_doc { + () => {}; +} +/// docs +macro mac_crate_doc { + () => {}, +} + +#[doc(hidden)] +fn fn_crate_hidden() {} +#[doc(hidden)] +const CONST_CRATE_HIDDEN: u32 = 0; +#[doc(hidden)] +static STATIC_CRATE_HIDDEN: u32 = 0; +#[doc(hidden)] +type TyAliasCrateHidden = u32; +#[doc(hidden)] +trait TraitAliasCrateHidden = Iterator; +#[doc(hidden)] +struct StructCrateHidden; +#[doc(hidden)] +struct StructFieldCrateHidden { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + pub _f5: u32, + /// docs + pub _f6: u32, + _f7: u32, + /// docs + _f8: u32, +} +#[doc(hidden)] +struct StructTupleCrateHidden(u32, pub u32); +#[doc(hidden)] +enum EnumCrateHidden { + V1, + V2(u32), + V3 { + f1: u32, + /// docs + f2: u32, + }, + V4, + V5(u32), + /// docs + V6 { + f1: u32, + /// docs + f2: u32, + }, +} +#[doc(hidden)] +union UnionCrateHidden { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + pub _f5: u32, + /// docs + pub _f6: u32, + _f7: u32, + /// docs + _f8: u32, +} +#[doc(hidden)] +impl StructFieldCrateHidden { + pub fn f1() {} + pub const C1: u32 = 0; + /// docs + pub fn f2() {} + /// docs + pub const C2: u32 = 0; + fn f3() {} + const C3: u32 = 0; + /// docs + fn f4() {} + /// docs + const C4: u32 = 0; +} +#[doc(hidden)] +trait TraitCrateHidden { + fn f1(); + fn f2() {} + const C1: u32; + const C2: u32 = 0; + type T1; + /// docs + fn f3(); + /// docs + fn f4() {} + /// docs + const C3: u32; + /// docs + const C4: u32 = 0; + /// docs + type T2; +} +#[doc(hidden)] +macro_rules! mac_rules_crate_hidden { + () => {}; +} +#[doc(hidden)] +macro mac_crate_hidden { + () => {}, +} + +#[expect(clippy::missing_docs_in_private_items)] +fn fn_crate_expect() {} +#[expect(clippy::missing_docs_in_private_items)] +const CONST_CRATE_EXPECT: u32 = 0; +#[expect(clippy::missing_docs_in_private_items)] +static STATIC_CRATE_EXPECT: u32 = 0; +#[expect(clippy::missing_docs_in_private_items)] +type TyAliasCrateExpect = u32; +#[expect(clippy::missing_docs_in_private_items)] +trait TraitAliasCrateExpect = Iterator; +#[expect(clippy::missing_docs_in_private_items)] +struct StructCrateExpect; +#[expect(clippy::missing_docs_in_private_items)] +struct StructFieldCrateExpect { + #[expect(clippy::missing_docs_in_private_items)] + pub f1: u32, + /// docs + pub f2: u32, + #[expect(clippy::missing_docs_in_private_items)] + f3: u32, + /// docs + f4: u32, +} +#[expect(clippy::missing_docs_in_private_items)] +struct StructTupleCrateExpect(u32, pub u32); +#[expect(clippy::missing_docs_in_private_items)] +enum EnumCrateExpect { + #[expect(clippy::missing_docs_in_private_items)] + V1, + #[expect(clippy::missing_docs_in_private_items)] + V2(u32), + #[expect(clippy::missing_docs_in_private_items)] + V3 { + #[expect(clippy::missing_docs_in_private_items)] + f1: u32, + /// docs + f2: u32, + }, + /// docs + V4, + /// docs + V5(u32), + /// docs + V6 { + #[expect(clippy::missing_docs_in_private_items)] + f1: u32, + /// docs + f2: u32, + }, +} +#[expect(clippy::missing_docs_in_private_items)] +union UnionCrateExpect { + #[expect(clippy::missing_docs_in_private_items)] + pub f1: u32, + /// docs + pub f2: u32, + #[expect(clippy::missing_docs_in_private_items)] + f3: u32, + /// docs + f4: u32, +} +impl StructFieldCrateExpect { + #[expect(clippy::missing_docs_in_private_items)] + pub fn f1() {} + #[expect(clippy::missing_docs_in_private_items)] + pub const C1: u32 = 0; + #[expect(clippy::missing_docs_in_private_items)] + fn f2() {} + #[expect(clippy::missing_docs_in_private_items)] + const C2: u32 = 0; +} +#[expect(clippy::missing_docs_in_private_items)] +trait TraitCrateExpect { + #[expect(clippy::missing_docs_in_private_items)] + fn f1(); + #[expect(clippy::missing_docs_in_private_items)] + fn f2() {} + #[expect(clippy::missing_docs_in_private_items)] + const C1: u32; + #[expect(clippy::missing_docs_in_private_items)] + const C2: u32 = 0; + #[expect(clippy::missing_docs_in_private_items)] + type T1; +} +#[expect(clippy::missing_docs_in_private_items)] +macro_rules! mac_rules_crate_expect { + () => {}; +} +#[expect(clippy::missing_docs_in_private_items)] +macro mac_crate_expect { + () => {}, +} + +pub mod mod_pub { + pub fn f1() {} + pub struct S1 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + pub enum E1 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + pub const C1: u32 = 0; + + /// docs + pub fn f2() {} + /// docs + pub struct S2 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + /// docs + pub enum E2 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + /// docs + pub const C2: u32 = 0; + + fn f3() {} //~[default,allow_unused] missing_docs_in_private_items + // + //~[default,allow_unused]v missing_docs_in_private_items + struct S3 { + pub f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + //~[default,allow_unused]v missing_docs_in_private_items + enum E3 { + //~[default,allow_unused]v missing_docs_in_private_items + V1 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + } + const C3: u32 = 0; //~[default,allow_unused] missing_docs_in_private_items + + /// docs + fn f4() {} + /// docs + struct S4 { + pub f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + /// docs + enum E4 { + //~[default,allow_unused]v missing_docs_in_private_items + V1 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + } + /// docs + const C4: u32 = 0; +} + +//~v missing_docs_in_private_items +mod mod_crate { + pub fn f1() {} //~ missing_docs_in_private_items + // + //~v missing_docs_in_private_items + pub struct S1 { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + //~v missing_docs_in_private_items + pub enum E1 { + //~v missing_docs_in_private_items + V1 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + } + pub const C1: u32 = 0; //~ missing_docs_in_private_items + + /// docs + pub fn f2() {} + /// docs + pub struct S2 { + pub f1: u32, //~ missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + /// docs + pub enum E2 { + //~v missing_docs_in_private_items + V1 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~ missing_docs_in_private_items + /// docs + f2: u32, + }, + } + /// docs + pub const C2: u32 = 0; + + fn f3() {} //~[default,allow_unused] missing_docs_in_private_items + // + //~[default,allow_unused]v missing_docs_in_private_items + struct S3 { + pub f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + //~[default,allow_unused]v missing_docs_in_private_items + enum E3 { + //~[default,allow_unused]v missing_docs_in_private_items + V1 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + } + const C3: u32 = 0; //~[default,allow_unused] missing_docs_in_private_items + + /// docs + fn f4() {} + /// docs + struct S4 { + pub f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + pub f2: u32, + f3: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f4: u32, + } + /// docs + enum E4 { + //~[default,allow_unused]v missing_docs_in_private_items + V1 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, //~[default,allow_unused] missing_docs_in_private_items + /// docs + f2: u32, + }, + } + /// docs + const C4: u32 = 0; +} + +/// docs +mod mod_crate_doc {} + +#[doc(hidden)] +mod mod_crate_hidden { + pub fn f1() {} + pub struct S1 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + } + pub enum E1 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + pub const C1: u32 = 0; + + /// docs + pub fn f2() {} + /// docs + pub struct S2 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + } + /// docs + pub enum E2 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + /// docs + pub const C2: u32 = 0; + + fn f3() {} + struct S3 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + } + enum E3 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + const C3: u32 = 0; + + /// docs + fn f4() {} + /// docs + struct S4 { + pub f1: u32, + /// docs + pub f2: u32, + f3: u32, + /// docs + f4: u32, + } + /// docs + enum E4 { + V1 { + f1: u32, + /// docs + f2: u32, + }, + /// docs + V2 { + f1: u32, + /// docs + f2: u32, + }, + } + /// docs + const C4: u32 = 0; +} + +#[expect(clippy::missing_docs_in_private_items)] +mod mod_crate_expect {} + +#[doc = "docs"] +mod explicit_doc_attr {} + +with_span! { + sp + fn fn_pm() {} + const CONST_PM: u32 = 0; + static STATIC_PM: u32 = 0; + type TyAliasPm = u32; + trait TraitAliasPm = Iterator; + struct StructPm; + struct StructFieldPm { + pub f1: u32, + f2: u32, + pub _f3: u32, + _f4: u32, + } + struct StructTuplePm(u32, pub u32); + enum EnumPm { + V1, + V2(u32), + V3 { f1: u32, }, + } + union UnionPm { + pub f1: u32, + f2: u32, + pub _f3: u32, + _f4: u32, + } + impl StructFieldPm { + pub fn f1() {} + pub const C1: u32 = 0; + fn f2() {} + const C2: u32 = 0; + } + trait TraitPm { + fn f1(); + fn f2() {} + const C1: u32; + const C2: u32 = 0; + type T1; + } + impl TraitPm for StructPm { + fn f1() {} + const C1: u32 = 0; + type T1 = u32; + } + macro_rules! mac_rules_pm { + () => {}; + } + macro mac_pm { + () => {}, + } + mod mod_pm {} +} + +external! { + fn fn_external() {} + const CONST_EXTERNAL: u32 = 0; + static STATIC_EXTERNAL: u32 = 0; + type TyAliasExternal = u32; + trait TraitAliasExternal = Iterator; + struct StructExternal; + struct StructFieldExternal { + pub f1: u32, + f2: u32, + pub _f3: u32, + _f4: u32, + } + struct StructTupleExternal(u32, pub u32); + enum EnumExternal { + V1, + V2(u32), + V3 { f1: u32, }, + } + union UnionExternal { + pub f1: u32, + f2: u32, + pub _f3: u32, + _f4: u32, + } + impl StructFieldExternal { + pub fn f1() {} + pub const C1: u32 = 0; + fn f2() {} + const C2: u32 = 0; + } + trait TraitExternal { + fn f1(); + fn f2() {} + const C1: u32; + const C2: u32 = 0; + type T1; + } + impl TraitExternal for StructExternal { + fn f1() {} + const C1: u32 = 0; + type T1 = u32; + } + macro_rules! mac_rules_external { + () => {}; + } + macro mac_external { + () => {}, + } + mod mod_external {} +} + +pub const _: () = {}; +const _: () = {}; + +/// docs +fn fn_with_items() { + fn f() {} + type T = u32; + struct S { + f1: u32, + f2: u32, + } + enum E { + V { f: u32 }, + } + impl S { + fn f() {} + const C: u32 = 0; + } + const C: u32 = 0; + static ST: u32 = 0; + trait Tr { + fn f(); + type T; + const C: u32; + } + trait Tr2 = Tr; + mod m {} + macro_rules! m2 { + () => {}; + } + macro m3 { () => {}, } + union U { + f: u32, + } +} +/// docs +const CONST_WITH_ITEMS: () = { + fn f() {} +}; +/// docs +static STATIC_WITH_ITEMS: () = { + fn f() {} +}; +/// docs +trait TraitWithItems { + /// docs + fn f() { + fn f() {} + } + /// docs + const C: () = { + fn f() {} + }; +} +/// docs +struct StructWithItems; +impl StructWithItems { + /// docs + fn f() { + fn f() {} + } + /// docs + const C: () = { + fn f() {} + }; +} +/// docs +type TypeAliasWithItems = [u32; { + fn f() {} + 1 +}]; + +/// docs +mod with_reexports { + pub fn f1_reexport() {} + pub struct S1Reexport { + pub f1: u32, + f2: u32, //~[default,allow_unused] missing_docs_in_private_items + pub(crate) f3: u32, //~ missing_docs_in_private_items + /// docs + f4: u32, + } + + /// docs + mod m1 { + pub(crate) fn f2() {} //~[default,allow_unused] missing_docs_in_private_items + + //~v missing_docs_in_private_items + pub enum E1 { + V1Reexport, + V2, //~ missing_docs_in_private_items + } + + pub struct S2; //~ missing_docs_in_private_items + pub fn f3_reexport() -> S2 { + S2 + } + } + pub use m1::E1::{V1Reexport, V2}; + use m1::f2; + pub use m1::f3_reexport; +} +pub use with_reexports::{S1Reexport, V1Reexport, f1_reexport, f3_reexport}; + +external! { + mod mod_generated { + $(type T = u32;) + struct S { + $(f1: u32,) + f2: u32, + } + pub fn f() {} + $(pub fn f2() {}) //~ missing_docs_in_private_items + #[doc(hidden)] + $(pub fn f3() {}) + } +} + +/// docs +mod mod_with_hidden { + #[doc(hidden)] + pub mod m { + pub struct S { + #[doc(hidden)] + pub f: u32, + } + #[automatically_derived] + impl S { + #[doc(hidden)] + pub fn f() {} + pub const C: () = { + #[automatically_derived] + impl S { + #[doc(hidden)] + pub fn f2() { + mod m { + pub(crate) union U { + pub f: u32, + } + } + } + } + }; + } + } + #[doc(hidden)] + pub(crate) fn f() {} +} + +/// docs +struct WithProject { + /// docs + a: u32, + /// docs + b: u32, +} +with_span! { + span + const _: () = { + // Similar output to pin_project + struct Project<'a> { + $(a: &'a u32), + $(b: &'a u32), + } + impl $(WithProject) { + fn project(&self) -> Project<'_> { + Project { + a: &self.a, + b: &self.b, + } + } + } + }; +} + +external! { + mod mod_mac_with_pub {$( + struct DerivedFromInput; + impl DerivedFromInput { + pub fn foo() {} + } + pub struct VisFromOutside; //~ missing_docs_in_private_items + )} +} diff --git a/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs b/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs deleted file mode 100644 index 6a1d2b51abc8..000000000000 --- a/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! this is crate -#![allow(missing_docs)] -#![allow(clippy::struct_field_names)] -#![warn(clippy::missing_docs_in_private_items)] - -/// this is mod -mod my_mod { - /// some docs - fn priv_with_docs() {} - fn priv_no_docs() {} - /// some docs - pub(crate) fn crate_with_docs() {} - pub(crate) fn crate_no_docs() {} - //~^ missing_docs_in_private_items - /// some docs - pub(super) fn super_with_docs() {} - pub(super) fn super_no_docs() {} - //~^ missing_docs_in_private_items - - mod my_sub { - /// some docs - fn sub_priv_with_docs() {} - fn sub_priv_no_docs() {} - /// some docs - pub(crate) fn sub_crate_with_docs() {} - pub(crate) fn sub_crate_no_docs() {} - //~^ missing_docs_in_private_items - /// some docs - pub(super) fn sub_super_with_docs() {} - pub(super) fn sub_super_no_docs() {} - } - - /// some docs - pub(crate) struct CrateStructWithDocs { - /// some docs - pub(crate) crate_field_with_docs: (), - pub(crate) crate_field_no_docs: (), - //~^ missing_docs_in_private_items - /// some docs - priv_field_with_docs: (), - priv_field_no_docs: (), - } - - pub(crate) struct CrateStructNoDocs { - //~^ missing_docs_in_private_items - /// some docs - pub(crate) crate_field_with_docs: (), - pub(crate) crate_field_no_docs: (), - //~^ missing_docs_in_private_items - /// some docs - priv_field_with_docs: (), - priv_field_no_docs: (), - } -} - -/// some docs -type CrateTypedefWithDocs = String; -type CrateTypedefNoDocs = String; -//~^ missing_docs_in_private_items -/// some docs -pub type PubTypedefWithDocs = String; -pub type PubTypedefNoDocs = String; - -fn main() { - my_mod::crate_with_docs(); - my_mod::crate_no_docs(); -} diff --git a/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr b/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr deleted file mode 100644 index 0d70276de42d..000000000000 --- a/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: missing documentation for a function - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:13:5 - | -LL | pub(crate) fn crate_no_docs() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` - -error: missing documentation for a function - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:17:5 - | -LL | pub(super) fn super_no_docs() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:26:9 - | -LL | pub(crate) fn sub_crate_no_docs() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a struct field - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:37:9 - | -LL | pub(crate) crate_field_no_docs: (), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a struct - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:44:5 - | -LL | / pub(crate) struct CrateStructNoDocs { -LL | | -LL | | /// some docs -LL | | pub(crate) crate_field_with_docs: (), -... | -LL | | priv_field_no_docs: (), -LL | | } - | |_____^ - -error: missing documentation for a struct field - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:48:9 - | -LL | pub(crate) crate_field_no_docs: (), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a type alias - --> tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs:58:1 - | -LL | type CrateTypedefNoDocs = String; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 7 previous errors - diff --git a/tests/ui-toml/result_large_err/clippy.toml b/tests/ui-toml/result_large_err/clippy.toml index df505ed9672a..80eeac0f9870 100644 --- a/tests/ui-toml/result_large_err/clippy.toml +++ b/tests/ui-toml/result_large_err/clippy.toml @@ -1 +1,2 @@ large-error-threshold = 512 +large-error-ignored = ["result_large_err::IgnoredError", "result_large_err::IgnoredErrorEnum"] diff --git a/tests/ui-toml/result_large_err/result_large_err.rs b/tests/ui-toml/result_large_err/result_large_err.rs index dea4d61a96bf..170f37db7593 100644 --- a/tests/ui-toml/result_large_err/result_large_err.rs +++ b/tests/ui-toml/result_large_err/result_large_err.rs @@ -1,4 +1,6 @@ +//@compile-flags: --crate-name result_large_err #![warn(clippy::result_large_err)] +#![allow(clippy::large_enum_variant)] fn f() -> Result<(), [u8; 511]> { todo!() @@ -7,4 +9,22 @@ fn f2() -> Result<(), [u8; 512]> { //~^ ERROR: the `Err`-variant returned from this function is very large todo!() } + +struct IgnoredError { + inner: [u8; 512], +} + +fn f3() -> Result<(), IgnoredError> { + todo!() +} + +enum IgnoredErrorEnum { + V1, + V2 { inner: [u8; 512] }, +} + +fn f4() -> Result<(), IgnoredErrorEnum> { + todo!() +} + fn main() {} diff --git a/tests/ui-toml/result_large_err/result_large_err.stderr b/tests/ui-toml/result_large_err/result_large_err.stderr index 656ce7ab7f2f..7e5954f885b8 100644 --- a/tests/ui-toml/result_large_err/result_large_err.stderr +++ b/tests/ui-toml/result_large_err/result_large_err.stderr @@ -1,5 +1,5 @@ error: the `Err`-variant returned from this function is very large - --> tests/ui-toml/result_large_err/result_large_err.rs:6:12 + --> tests/ui-toml/result_large_err/result_large_err.rs:8:12 | LL | fn f2() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 2d9503c5ac53..d5040f4a39bf 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -50,6 +50,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability inherent-impl-lint-scope + large-error-ignored large-error-threshold lint-commented-code literal-representation-threshold @@ -147,6 +148,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability inherent-impl-lint-scope + large-error-ignored large-error-threshold lint-commented-code literal-representation-threshold @@ -244,6 +246,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability inherent-impl-lint-scope + large-error-ignored large-error-threshold lint-commented-code literal-representation-threshold diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs index de699309b16d..1cd45685609f 100644 --- a/tests/ui/blanket_clippy_restriction_lints.rs +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -3,7 +3,7 @@ #![warn(clippy::blanket_clippy_restriction_lints)] -//! Test that the whole restriction group is not enabled +//! Test that the whole restriction group is not enabled. #![warn(clippy::restriction)] //~^ blanket_clippy_restriction_lints #![deny(clippy::restriction)] diff --git a/tests/ui/byte_char_slices.fixed b/tests/ui/byte_char_slices.fixed index b0c1b1f034b4..87934d6362f7 100644 --- a/tests/ui/byte_char_slices.fixed +++ b/tests/ui/byte_char_slices.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::byte_char_slices)] fn main() { diff --git a/tests/ui/byte_char_slices.rs b/tests/ui/byte_char_slices.rs index 0d6953dda97e..0de7cf66fda8 100644 --- a/tests/ui/byte_char_slices.rs +++ b/tests/ui/byte_char_slices.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::byte_char_slices)] fn main() { diff --git a/tests/ui/byte_char_slices.stderr b/tests/ui/byte_char_slices.stderr index 2556aa9c0f76..c1b7e4ca2f17 100644 --- a/tests/ui/byte_char_slices.stderr +++ b/tests/ui/byte_char_slices.stderr @@ -1,5 +1,5 @@ error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:5:15 + --> tests/ui/byte_char_slices.rs:4:15 | LL | let bad = &[b'a', b'b', b'c']; | ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"` @@ -8,25 +8,25 @@ LL | let bad = &[b'a', b'b', b'c']; = help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:7:18 + --> tests/ui/byte_char_slices.rs:6:18 | LL | let quotes = &[b'"', b'H', b'i']; | ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:9:18 + --> tests/ui/byte_char_slices.rs:8:18 | LL | let quotes = &[b'\'', b'S', b'u', b'p']; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"` error: can be more succinctly written as a byte str - --> tests/ui/byte_char_slices.rs:11:19 + --> tests/ui/byte_char_slices.rs:10:19 | LL | let escapes = &[b'\x42', b'E', b's', b'c']; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"` error: useless use of `vec!` - --> tests/ui/byte_char_slices.rs:15:16 + --> tests/ui/byte_char_slices.rs:14:16 | LL | let good = vec![b'a', b'a']; | ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']` diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index fab02bf7b24e..ff2791c45730 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -582,3 +582,13 @@ mod issue14150 { //~^ cast_possible_wrap } } + +fn issue16045() { + fn f() -> Result<(), ()> { + let val = Ok::<_, ()>(0u8); + _ = val? as i8; + //~^ cast_possible_wrap + + Ok(()) + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 8c48855123f9..0ff1dc11c3ac 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -764,5 +764,11 @@ error: casting `u8` to `i8` may wrap around the value LL | _ = 1u8 as i8; | ^^^^^^^^^ -error: aborting due to 94 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:589:13 + | +LL | _ = val? as i8; + | ^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `val?.cast_signed()` + +error: aborting due to 95 previous errors diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index 7d0bcc547a42..d1db2e67e269 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,27 +1,19 @@ -#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow( - clippy::if_same_then_else, - clippy::branches_sharing_code, - clippy::unnecessary_literal_unwrap -)] +#![warn(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![expect(clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); let y: Result<(), ()> = Ok(()); if x.is_ok() && y.is_err() { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap - // will panic x.unwrap_err(); //~^ panicking_unwrap - // will panic y.unwrap(); //~^ panicking_unwrap - // unnecessary y.unwrap_err(); //~^ unnecessary_unwrap } else { @@ -37,45 +29,35 @@ fn test_complex_conditions() { x.unwrap(); y.unwrap(); } else { - // will panic x.unwrap(); //~^ panicking_unwrap - // unnecessary x.unwrap_err(); //~^ unnecessary_unwrap - // will panic y.unwrap(); //~^ panicking_unwrap - // unnecessary y.unwrap_err(); //~^ unnecessary_unwrap } let z: Result<(), ()> = Ok(()); if x.is_ok() && !(y.is_ok() || z.is_err()) { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap - // will panic x.unwrap_err(); //~^ panicking_unwrap - // will panic y.unwrap(); //~^ panicking_unwrap - // unnecessary y.unwrap_err(); //~^ unnecessary_unwrap - // unnecessary z.unwrap(); //~^ unnecessary_unwrap - // will panic z.unwrap_err(); //~^ panicking_unwrap } @@ -85,27 +67,21 @@ fn test_complex_conditions() { y.unwrap(); z.unwrap(); } else { - // will panic x.unwrap(); //~^ panicking_unwrap - // unnecessary x.unwrap_err(); //~^ unnecessary_unwrap - // unnecessary y.unwrap(); //~^ unnecessary_unwrap - // will panic y.unwrap_err(); //~^ panicking_unwrap - // will panic z.unwrap(); //~^ panicking_unwrap - // unnecessary z.unwrap_err(); //~^ unnecessary_unwrap } diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index d3905850c970..e154e3c35dc9 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,21 +1,17 @@ error: called `unwrap` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:13:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { | --------- the check is happening here -LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ | = help: try using `if let` or `match` -note: the lint level is defined here - --> tests/ui/checked_unwrap/complex_conditionals.rs:1:35 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap)]` error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:17:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { | --------- because of this check @@ -23,14 +19,11 @@ LL | if x.is_ok() && y.is_err() { LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/checked_unwrap/complex_conditionals.rs:1:9 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::panicking-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panicking_unwrap)]` error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:21:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:14:9 | LL | if x.is_ok() && y.is_err() { | ---------- because of this check @@ -39,7 +32,7 @@ LL | y.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `y` after checking its variant with `is_err` - --> tests/ui/checked_unwrap/complex_conditionals.rs:25:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:17:9 | LL | if x.is_ok() && y.is_err() { | ---------- the check is happening here @@ -50,7 +43,7 @@ LL | y.unwrap_err(); = help: try using `if let` or `match` error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:41:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:32:9 | LL | if x.is_ok() || y.is_ok() { | --------- because of this check @@ -59,7 +52,7 @@ LL | x.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:45:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:35:9 | LL | if x.is_ok() || y.is_ok() { | --------- the check is happening here @@ -70,7 +63,7 @@ LL | x.unwrap_err(); = help: try using `if let` or `match` error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:49:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:38:9 | LL | if x.is_ok() || y.is_ok() { | --------- because of this check @@ -79,7 +72,7 @@ LL | y.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `y` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:53:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:41:9 | LL | if x.is_ok() || y.is_ok() { | --------- the check is happening here @@ -90,18 +83,17 @@ LL | y.unwrap_err(); = help: try using `if let` or `match` error: called `unwrap` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:59:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:46:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- the check is happening here -LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ | = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:63:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:49:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- because of this check @@ -110,7 +102,7 @@ LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:67:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:52:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- because of this check @@ -119,7 +111,7 @@ LL | y.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `y` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:71:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:55:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- the check is happening here @@ -130,7 +122,7 @@ LL | y.unwrap_err(); = help: try using `if let` or `match` error: called `unwrap` on `z` after checking its variant with `is_err` - --> tests/ui/checked_unwrap/complex_conditionals.rs:75:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:58:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | ---------- the check is happening here @@ -141,7 +133,7 @@ LL | z.unwrap(); = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:79:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:61:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | ---------- because of this check @@ -150,7 +142,7 @@ LL | z.unwrap_err(); | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:89:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:70:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | --------- because of this check @@ -159,7 +151,7 @@ LL | x.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:93:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:73:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | --------- the check is happening here @@ -170,7 +162,7 @@ LL | x.unwrap_err(); = help: try using `if let` or `match` error: called `unwrap` on `y` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/complex_conditionals.rs:97:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:76:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | --------- the check is happening here @@ -181,7 +173,7 @@ LL | y.unwrap(); = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:101:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:79:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | --------- because of this check @@ -190,7 +182,7 @@ LL | y.unwrap_err(); | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals.rs:105:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:82:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | ---------- because of this check @@ -199,7 +191,7 @@ LL | z.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `z` after checking its variant with `is_err` - --> tests/ui/checked_unwrap/complex_conditionals.rs:109:9 + --> tests/ui/checked_unwrap/complex_conditionals.rs:85:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { | ---------- the check is happening here diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 7635f848cb34..6789e7c262b3 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,19 +1,14 @@ -#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow( - clippy::if_same_then_else, - clippy::branches_sharing_code, - clippy::unnecessary_literal_unwrap -)] //@no-rustfix: has placeholders +#![warn(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![expect(clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap)] + fn test_nested() { fn nested() { let x = Some(()); if x.is_some() { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap } else { - // will panic x.unwrap(); //~^ panicking_unwrap } diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index 329be4d36621..7e4ef049f4a5 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,20 +1,16 @@ error: called `unwrap` on `x` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:13:13 + --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:9:13 | LL | if x.is_some() { | -------------- help: try: `if let Some() = x` -LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:1:35 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap)]` error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:17:13 + --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:12:13 | LL | if x.is_some() { | ----------- because of this check @@ -22,11 +18,8 @@ LL | if x.is_some() { LL | x.unwrap(); | ^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:1:9 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::panicking-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panicking_unwrap)]` error: aborting due to 2 previous errors diff --git a/tests/ui/checked_unwrap/if_let_chains.rs b/tests/ui/checked_unwrap/if_let_chains.rs index cfa7715965cd..5c20ebb80024 100644 --- a/tests/ui/checked_unwrap/if_let_chains.rs +++ b/tests/ui/checked_unwrap/if_let_chains.rs @@ -1,5 +1,5 @@ //@require-annotations-for-level: ERROR -#![deny(clippy::unnecessary_unwrap)] +#![warn(clippy::unnecessary_unwrap)] #[clippy::msrv = "1.85"] fn if_let_chains_unsupported(a: Option, b: Option) { diff --git a/tests/ui/checked_unwrap/if_let_chains.stderr b/tests/ui/checked_unwrap/if_let_chains.stderr index 8a4137de37a3..801b074fc277 100644 --- a/tests/ui/checked_unwrap/if_let_chains.stderr +++ b/tests/ui/checked_unwrap/if_let_chains.stderr @@ -8,11 +8,8 @@ LL | println!("the value of a is {}", a.unwrap()); | ^^^^^^^^^^ | = help: try using `match` -note: the lint level is defined here - --> tests/ui/checked_unwrap/if_let_chains.rs:2:9 - | -LL | #![deny(clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap)]` error: called `unwrap` on `a` after checking its variant with `is_none` --> tests/ui/checked_unwrap/if_let_chains.rs:20:42 diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index bba264080b40..c6476a7507a1 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,15 +1,15 @@ //@no-rustfix: has placeholders -#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow( +#![warn(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![expect( clippy::if_same_then_else, clippy::branches_sharing_code, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::self_assignment )] macro_rules! m { ($a:expr) => { if $a.is_some() { - // unnecessary $a.unwrap(); //~^ unnecessary_unwrap } @@ -43,90 +43,71 @@ macro_rules! checks_some { fn main() { let x = Some(()); if x.is_some() { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap - // unnecessary x.expect("an error message"); //~^ unnecessary_unwrap } else { - // will panic x.unwrap(); //~^ panicking_unwrap - // will panic x.expect("an error message"); //~^ panicking_unwrap } if x.is_none() { - // will panic x.unwrap(); //~^ panicking_unwrap } else { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap } m!(x); - // ok checks_in_param!(x.is_some(), x.unwrap()); - // ok checks_unwrap!(x, x.unwrap()); - // ok checks_some!(x.is_some(), x); let mut x: Result<(), ()> = Ok(()); if x.is_ok() { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap - // unnecessary x.expect("an error message"); //~^ unnecessary_unwrap - // will panic x.unwrap_err(); //~^ panicking_unwrap } else { - // will panic x.unwrap(); //~^ panicking_unwrap - // will panic x.expect("an error message"); //~^ panicking_unwrap - // unnecessary x.unwrap_err(); //~^ unnecessary_unwrap } if x.is_err() { - // will panic x.unwrap(); //~^ panicking_unwrap - // unnecessary x.unwrap_err(); //~^ unnecessary_unwrap } else { - // unnecessary x.unwrap(); //~^ unnecessary_unwrap - // will panic x.unwrap_err(); //~^ panicking_unwrap } if x.is_ok() { x = Err(()); - // not unnecessary because of mutation of x + // not unnecessary because of mutation of `x` // it will always panic but the lint is not smart enough to see this (it only // checks if conditions). x.unwrap(); } else { x = Ok(()); - // not unnecessary because of mutation of x + // not unnecessary because of mutation of `x` // it will always panic but the lint is not smart enough to see this (it // only checks if conditions). x.unwrap_err(); @@ -175,13 +156,11 @@ fn issue11371() { //~^ panicking_unwrap } - // This should not lint. Statics are, at the time of writing, not linted on anyway, - // but if at some point they are supported by this lint, it should correctly see that - // `X` is being mutated and not suggest `if let Some(..) = X {}` + // This should not lint and suggest `if let Some(..) = X {}`, as `X` is being mutated static mut X: Option = Some(123); unsafe { + #[expect(static_mut_refs)] if X.is_some() { - //~^ ERROR: creating a shared reference X = None; X.unwrap(); } @@ -299,17 +278,197 @@ fn check_expect() { let x = Some(()); if x.is_some() { #[expect(clippy::unnecessary_unwrap)] - // unnecessary x.unwrap(); #[expect(clippy::unnecessary_unwrap)] - // unnecessary x.expect("an error message"); } else { #[expect(clippy::panicking_unwrap)] - // will panic x.unwrap(); #[expect(clippy::panicking_unwrap)] - // will panic x.expect("an error message"); } } + +fn partial_moves() { + fn borrow_option(_: &Option<()>) {} + + let x = Some(()); + // Using `if let Some(o) = x` won't work here, as `borrow_option` will try to borrow a moved value + if x.is_some() { + borrow_option(&x); + x.unwrap(); + //~^ unnecessary_unwrap + } + // This is fine though, as `if let Some(o) = &x` won't move `x` + if x.is_some() { + borrow_option(&x); + x.as_ref().unwrap(); + //~^ unnecessary_unwrap + } +} + +fn issue15321() { + struct Soption { + option: Option, + other: bool, + } + let mut sopt = Soption { + option: Some(true), + other: true, + }; + // Lint: nothing was mutated + let _res = if sopt.option.is_some() { + sopt.option.unwrap() + //~^ unnecessary_unwrap + } else { + sopt.option.unwrap() + //~^ panicking_unwrap + }; + // Lint: an unrelated field was mutated + let _res = if sopt.option.is_some() { + sopt.other = false; + sopt.option.unwrap() + //~^ unnecessary_unwrap + } else { + sopt.other = false; + sopt.option.unwrap() + //~^ panicking_unwrap + }; + // No lint: the whole local was mutated + let _res = if sopt.option.is_some() { + sopt = sopt; + sopt.option.unwrap() + } else { + sopt.option = None; + sopt.option.unwrap() + }; + // No lint: the field we're looking at was mutated + let _res = if sopt.option.is_some() { + sopt = sopt; + sopt.option.unwrap() + } else { + sopt.option = None; + sopt.option.unwrap() + }; + + struct Toption(Option, bool); + let mut topt = Toption(Some(true), true); + // Lint: nothing was mutated + let _res = if topt.0.is_some() { + topt.0.unwrap() + //~^ unnecessary_unwrap + } else { + topt.0.unwrap() + //~^ panicking_unwrap + }; + // Lint: an unrelated field was mutated + let _res = if topt.0.is_some() { + topt.1 = false; + topt.0.unwrap() + //~^ unnecessary_unwrap + } else { + topt.1 = false; + topt.0.unwrap() + //~^ panicking_unwrap + }; + // No lint: the whole local was mutated + let _res = if topt.0.is_some() { + topt = topt; + topt.0.unwrap() + } else { + topt = topt; + topt.0.unwrap() + }; + // No lint: the field we're looking at was mutated + let _res = if topt.0.is_some() { + topt.0 = None; + topt.0.unwrap() + } else { + topt.0 = None; + topt.0.unwrap() + }; + + // Nested field accesses get linted as well + struct Soption2 { + other: bool, + option: Soption, + } + let mut sopt2 = Soption2 { + other: true, + option: Soption { + option: Some(true), + other: true, + }, + }; + // Lint: no fields were mutated + let _res = if sopt2.option.option.is_some() { + sopt2.option.option.unwrap() + //~^ unnecessary_unwrap + } else { + sopt2.option.option.unwrap() + //~^ panicking_unwrap + }; + // Lint: an unrelated outer field was mutated -- don't get confused by `Soption2.other` having the + // same `FieldIdx` of 1 as `Soption.option` + let _res = if sopt2.option.option.is_some() { + sopt2.other = false; + sopt2.option.option.unwrap() + //~^ unnecessary_unwrap + } else { + sopt2.other = false; + sopt2.option.option.unwrap() + //~^ panicking_unwrap + }; + // Lint: an unrelated inner field was mutated + let _res = if sopt2.option.option.is_some() { + sopt2.option.other = false; + sopt2.option.option.unwrap() + //~^ unnecessary_unwrap + } else { + sopt2.option.other = false; + sopt2.option.option.unwrap() + //~^ panicking_unwrap + }; + // Don't lint: the whole local was mutated + let _res = if sopt2.option.option.is_some() { + sopt2 = sopt2; + sopt2.option.option.unwrap() + } else { + sopt2 = sopt2; + sopt2.option.option.unwrap() + }; + // Don't lint: a parent field of the field we're looking at was mutated, and with that the + // field we're looking at + let _res = if sopt2.option.option.is_some() { + sopt2.option = sopt; + sopt2.option.option.unwrap() + } else { + sopt2.option = sopt; + sopt2.option.option.unwrap() + }; + // Don't lint: the field we're looking at was mutated directly + let _res = if sopt2.option.option.is_some() { + sopt2.option.option = None; + sopt2.option.option.unwrap() + } else { + sopt2.option.option = None; + sopt2.option.option.unwrap() + }; + + // Partial moves + fn borrow_toption(_: &Toption) {} + + // Using `if let Some(o) = topt.0` won't work here, as `borrow_toption` will try to borrow a + // partially moved value + if topt.0.is_some() { + borrow_toption(&topt); + topt.0.unwrap(); + //~^ unnecessary_unwrap + } + // This is fine though, as `if let Some(o) = &topt.0` won't (partially) move `topt` + if topt.0.is_some() { + borrow_toption(&topt); + topt.0.as_ref().unwrap(); + //~^ unnecessary_unwrap + } +} diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 2007a8595413..be979baa9fe4 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,20 +1,16 @@ error: called `unwrap` on `x` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:47:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:46:9 | LL | if x.is_some() { | -------------- help: try: `if let Some() = x` -LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/checked_unwrap/simple_conditionals.rs:2:35 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap)]` error: called `expect` on `x` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:51:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:49:9 | LL | if x.is_some() { | -------------- help: try: `if let Some() = x` @@ -23,40 +19,36 @@ LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:52:9 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.unwrap(); + | ^^^^^^^^^^ + | + = note: `-D clippy::panicking-unwrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panicking_unwrap)]` + +error: this call to `expect()` will always panic --> tests/ui/checked_unwrap/simple_conditionals.rs:55:9 | LL | if x.is_some() { | ----------- because of this check ... -LL | x.unwrap(); - | ^^^^^^^^^^ - | -note: the lint level is defined here - --> tests/ui/checked_unwrap/simple_conditionals.rs:2:9 - | -LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this call to `expect()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:59:9 - | -LL | if x.is_some() { - | ----------- because of this check -... LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:64:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:59:9 | LL | if x.is_none() { | ----------- because of this check -LL | // will panic LL | x.unwrap(); | ^^^^^^^^^^ error: called `unwrap` on `x` after checking its variant with `is_none` - --> tests/ui/checked_unwrap/simple_conditionals.rs:68:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:62:9 | LL | if x.is_none() { | -------------- help: try: `if let Some() = x` @@ -69,7 +61,6 @@ error: called `unwrap` on `x` after checking its variant with `is_some` | LL | if $a.is_some() { | --------------- help: try: `if let Some() = x` -LL | // unnecessary LL | $a.unwrap(); | ^^^^^^^^^^^ ... @@ -79,16 +70,15 @@ LL | m!(x); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: called `unwrap` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:81:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:71:9 | LL | if x.is_ok() { | ------------ help: try: `if let Ok() = x` -LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ error: called `expect` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:85:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:74:9 | LL | if x.is_ok() { | ------------ help: try: `if let Ok() = x` @@ -97,7 +87,7 @@ LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:89:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:77:9 | LL | if x.is_ok() { | --------- because of this check @@ -106,7 +96,7 @@ LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:93:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:80:9 | LL | if x.is_ok() { | --------- because of this check @@ -115,7 +105,7 @@ LL | x.unwrap(); | ^^^^^^^^^^ error: this call to `expect()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:97:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:83:9 | LL | if x.is_ok() { | --------- because of this check @@ -124,7 +114,7 @@ LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: called `unwrap_err` on `x` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:101:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:86:9 | LL | if x.is_ok() { | ------------ help: try: `if let Err() = x` @@ -133,16 +123,15 @@ LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:106:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:90:9 | LL | if x.is_err() { | ---------- because of this check -LL | // will panic LL | x.unwrap(); | ^^^^^^^^^^ error: called `unwrap_err` on `x` after checking its variant with `is_err` - --> tests/ui/checked_unwrap/simple_conditionals.rs:110:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:93:9 | LL | if x.is_err() { | ------------- help: try: `if let Err() = x` @@ -151,7 +140,7 @@ LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ error: called `unwrap` on `x` after checking its variant with `is_err` - --> tests/ui/checked_unwrap/simple_conditionals.rs:114:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:96:9 | LL | if x.is_err() { | ------------- help: try: `if let Ok() = x` @@ -160,7 +149,7 @@ LL | x.unwrap(); | ^^^^^^^^^^ error: this call to `unwrap_err()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:118:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:99:9 | LL | if x.is_err() { | ---------- because of this check @@ -169,58 +158,58 @@ LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ error: called `unwrap` on `option` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:143:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:124:9 | LL | if option.is_some() { | ------------------- help: try: `if let Some() = &option` LL | option.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:127:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:134:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = &result` +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:137:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:143:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = &mut option` +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap()` will always panic --> tests/ui/checked_unwrap/simple_conditionals.rs:146:9 | LL | if option.is_some() { | ---------------- because of this check ... -LL | option.as_ref().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: called `unwrap` on `result` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:153:9 - | -LL | if result.is_ok() { - | ----------------- help: try: `if let Ok() = &result` -LL | result.as_ref().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:156:9 - | -LL | if result.is_ok() { - | -------------- because of this check -... -LL | result.as_ref().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: called `unwrap` on `option` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:162:9 - | -LL | if option.is_some() { - | ------------------- help: try: `if let Some() = &mut option` -LL | option.as_mut().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:165:9 - | -LL | if option.is_some() { - | ---------------- because of this check -... LL | option.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: called `unwrap` on `result` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:171:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:152:9 | LL | if result.is_ok() { | ----------------- help: try: `if let Ok() = &mut result` @@ -228,7 +217,7 @@ LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:174:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:155:9 | LL | if result.is_ok() { | -------------- because of this check @@ -237,7 +226,7 @@ LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: called `unwrap` on `option` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:205:17 + --> tests/ui/checked_unwrap/simple_conditionals.rs:184:17 | LL | if option.is_some() { | ------------------- help: try: `if let Some() = &option` @@ -245,7 +234,7 @@ LL | let _ = option.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:208:17 + --> tests/ui/checked_unwrap/simple_conditionals.rs:187:17 | LL | if option.is_some() { | ---------------- because of this check @@ -254,7 +243,7 @@ LL | let _ = option.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: called `unwrap` on `result` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:216:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:195:9 | LL | if result.is_ok() { | ----------------- help: try: `if let Ok() = &result` @@ -263,7 +252,7 @@ LL | result.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:220:9 + --> tests/ui/checked_unwrap/simple_conditionals.rs:199:9 | LL | if result.is_ok() { | -------------- because of this check @@ -271,6 +260,40 @@ LL | if result.is_ok() { LL | result.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:225:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:228:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `r` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:234:17 + | +LL | if r.is_ok() { + | ------------ help: try: `if let Ok() = &r` +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:237:17 + | +LL | if r.is_ok() { + | --------- because of this check +... +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + error: called `unwrap` on `x` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:246:17 | @@ -288,42 +311,8 @@ LL | if x.is_some() { LL | _ = x.unwrap(); | ^^^^^^^^^^ -error: called `unwrap` on `r` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:255:17 - | -LL | if r.is_ok() { - | ------------ help: try: `if let Ok() = &r` -LL | _ = r.as_ref().unwrap(); - | ^^^^^^^^^^^^^^^^^^^ - -error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:258:17 - | -LL | if r.is_ok() { - | --------- because of this check -... -LL | _ = r.as_ref().unwrap(); - | ^^^^^^^^^^^^^^^^^^^ - -error: called `unwrap` on `x` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:267:17 - | -LL | if x.is_some() { - | -------------- help: try: `if let Some() = x` -LL | _ = x.unwrap(); - | ^^^^^^^^^^ - -error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:270:17 - | -LL | if x.is_some() { - | ----------- because of this check -... -LL | _ = x.unwrap(); - | ^^^^^^^^^^ - error: called `unwrap` on `option` after checking its variant with `is_some` - --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26 + --> tests/ui/checked_unwrap/simple_conditionals.rs:259:26 | LL | if option.is_some() { | ------------------- help: try: `if let Some() = option` @@ -331,7 +320,7 @@ LL | println!("{:?}", option.unwrap()); | ^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26 + --> tests/ui/checked_unwrap/simple_conditionals.rs:262:26 | LL | if option.is_some() { | ---------------- because of this check @@ -340,7 +329,7 @@ LL | println!("{:?}", option.unwrap()); | ^^^^^^^^^^^^^^^ error: called `unwrap` on `result` after checking its variant with `is_ok` - --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26 + --> tests/ui/checked_unwrap/simple_conditionals.rs:269:26 | LL | if result.is_ok() { | ----------------- help: try: `if let Ok() = result` @@ -348,7 +337,7 @@ LL | println!("{:?}", result.unwrap()); | ^^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26 + --> tests/ui/checked_unwrap/simple_conditionals.rs:272:26 | LL | if result.is_ok() { | -------------- because of this check @@ -356,15 +345,164 @@ LL | if result.is_ok() { LL | println!("{:?}", result.unwrap()); | ^^^^^^^^^^^^^^^ -error: creating a shared reference to mutable static - --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:299:9 | -LL | if X.is_some() { - | ^^^^^^^^^^^ shared reference to mutable static +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | borrow_option(&x); +LL | x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:305:9 | - = note: for more information, see - = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default +LL | if x.is_some() { + | -------------- help: try: `if let Some() = &x` +LL | borrow_option(&x); +LL | x.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 40 previous errors +error: called `unwrap` on `sopt.option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:321:9 + | +LL | let _res = if sopt.option.is_some() { + | ------------------------ help: try: `if let Some() = sopt.option` +LL | sopt.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:324:9 + | +LL | let _res = if sopt.option.is_some() { + | --------------------- because of this check +... +LL | sopt.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `sopt.option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:330:9 + | +LL | let _res = if sopt.option.is_some() { + | ------------------------ help: try: `if let Some() = sopt.option` +LL | sopt.other = false; +LL | sopt.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:334:9 + | +LL | let _res = if sopt.option.is_some() { + | --------------------- because of this check +... +LL | sopt.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `topt.0` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:358:9 + | +LL | let _res = if topt.0.is_some() { + | ------------------- help: try: `if let Some() = topt.0` +LL | topt.0.unwrap() + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:361:9 + | +LL | let _res = if topt.0.is_some() { + | ---------------- because of this check +... +LL | topt.0.unwrap() + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `topt.0` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:367:9 + | +LL | let _res = if topt.0.is_some() { + | ------------------- help: try: `if let Some() = topt.0` +LL | topt.1 = false; +LL | topt.0.unwrap() + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:371:9 + | +LL | let _res = if topt.0.is_some() { + | ---------------- because of this check +... +LL | topt.0.unwrap() + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `sopt2.option.option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:405:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | -------------------------------- help: try: `if let Some() = sopt2.option.option` +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:408:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | ----------------------------- because of this check +... +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `sopt2.option.option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:415:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | -------------------------------- help: try: `if let Some() = sopt2.option.option` +LL | sopt2.other = false; +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:419:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | ----------------------------- because of this check +... +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `sopt2.option.option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:425:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | -------------------------------- help: try: `if let Some() = sopt2.option.option` +LL | sopt2.option.other = false; +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:429:9 + | +LL | let _res = if sopt2.option.option.is_some() { + | ----------------------------- because of this check +... +LL | sopt2.option.option.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `topt.0` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:465:9 + | +LL | if topt.0.is_some() { + | ------------------- help: try: `if let Some() = topt.0` +LL | borrow_toption(&topt); +LL | topt.0.unwrap(); + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `topt.0` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:471:9 + | +LL | if topt.0.is_some() { + | ------------------- help: try: `if let Some() = &topt.0` +LL | borrow_toption(&topt); +LL | topt.0.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 57 previous errors diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed b/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed new file mode 100644 index 000000000000..95d65039440b --- /dev/null +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed @@ -0,0 +1,172 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] +#![warn(clippy::doc_paragraphs_missing_punctuation)] + +/// Returns the Answer to the Ultimate Question of Life, the Universe, and Everything. +//~^ doc_paragraphs_missing_punctuation +fn answer() -> i32 { + 42 +} + +/// The `Option` type. +//~^ doc_paragraphs_missing_punctuation +// Triggers even in the presence of another attribute. +#[derive(Debug)] +enum MyOption { + /// No value. + //~^ doc_paragraphs_missing_punctuation + None, + /// Some value of type `T`. + Some(T), +} + +// Triggers correctly even when interleaved with other attributes. +/// A multiline +#[derive(Debug)] +/// doc comment: +/// only the last line triggers the lint. +//~^ doc_paragraphs_missing_punctuation +enum Exceptions { + /// Question marks are fine? + QuestionMark, + /// Exclamation marks are fine! + ExclamationMark, + /// Ellipses are ok too… + Ellipsis, + /// HTML content is however not checked: + /// Raw HTML is allowed as well + RawHtml, + /// The raw HTML exception actually does the right thing to autolinks: + /// . + //~^ doc_paragraphs_missing_punctuation + MarkdownAutolink, + /// This table introduction ends with a colon: + /// + /// | Exception | Note | + /// | -------------- | ----- | + /// | Markdown table | A-ok | + MarkdownTable, + /// Here is a snippet. + //~^ doc_paragraphs_missing_punctuation + /// + /// ``` + /// // Code blocks are no issues. + /// ``` + CodeBlock, +} + +// Check the lint can be expected on a whole enum at once. +#[expect(clippy::doc_paragraphs_missing_punctuation)] +enum Char { + /// U+0000 + Null, + /// U+0001 + StartOfHeading, +} + +// Check the lint can be expected on a single variant without affecting others. +enum Char2 { + #[expect(clippy::doc_paragraphs_missing_punctuation)] + /// U+0000 + Null, + /// U+0001. + //~^ doc_paragraphs_missing_punctuation + StartOfHeading, +} + +mod module { + //! Works on + //! inner attributes too. + //~^ doc_paragraphs_missing_punctuation +} + +enum Trailers { + /// Sometimes the last sentence ends with parentheses (and that's ok). + ParensPassing, + /// (Sometimes the last sentence is in parentheses.) + SentenceInParensPassing, + /// **Sometimes the last sentence is in bold, and that's ok.** + DoubleStarPassing, + /// **But sometimes it is missing a period.** + //~^ doc_paragraphs_missing_punctuation + DoubleStarFailing, + /// _Sometimes the last sentence is in italics, and that's ok._ + UnderscorePassing, + /// _But sometimes it is missing a period._ + //~^ doc_paragraphs_missing_punctuation + UnderscoreFailing, + /// This comment ends with "a quote." + AmericanStyleQuotePassing, + /// This comment ends with "a quote". + BritishStyleQuotePassing, +} + +/// Doc comments can end with an [inline link](#anchor). +//~^ doc_paragraphs_missing_punctuation +struct InlineLink; + +/// Some doc comments contain [link reference definitions][spec]. +//~^ doc_paragraphs_missing_punctuation +/// +/// [spec]: https://spec.commonmark.org/0.31.2/#link-reference-definitions +struct LinkRefDefinition; + +// List items do not always need to end with a period. +enum UnorderedLists { + /// This list has an introductory sentence: + /// + /// - A list item + Dash, + /// + A list item + Plus, + /// * A list item + Star, +} + +enum OrderedLists { + /// 1. A list item + Dot, + /// 42) A list item + Paren, +} + +/// Doc comments with trailing blank lines are supported. +//~^ doc_paragraphs_missing_punctuation +/// +struct TrailingBlankLine; + +/// This doc comment has multiple paragraph. +/// This first paragraph is missing punctuation. +//~^ doc_paragraphs_missing_punctuation +/// +/// The second one as well +/// And it has multiple sentences. +//~^ doc_paragraphs_missing_punctuation +/// +/// Same for this third and last one. +//~^ doc_paragraphs_missing_punctuation +struct MultiParagraphDocComment; + +/// ``` +struct IncompleteBlockCode; + +/// This ends with a code `span`. +//~^ doc_paragraphs_missing_punctuation +struct CodeSpan; + +#[expect(clippy::empty_docs)] +/// +struct EmptyDocComment; + +/** + * Block doc comments work. + * + */ +//~^^^ doc_paragraphs_missing_punctuation +struct BlockDocComment; + +/// Sometimes a doc attribute is used for concatenation +/// ``` +#[doc = ""] +/// ``` +struct DocAttribute; diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.rs b/tests/ui/doc/doc_paragraphs_missing_punctuation.rs new file mode 100644 index 000000000000..35b74d7d13b9 --- /dev/null +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.rs @@ -0,0 +1,172 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] +#![warn(clippy::doc_paragraphs_missing_punctuation)] + +/// Returns the Answer to the Ultimate Question of Life, the Universe, and Everything +//~^ doc_paragraphs_missing_punctuation +fn answer() -> i32 { + 42 +} + +/// The `Option` type +//~^ doc_paragraphs_missing_punctuation +// Triggers even in the presence of another attribute. +#[derive(Debug)] +enum MyOption { + /// No value + //~^ doc_paragraphs_missing_punctuation + None, + /// Some value of type `T`. + Some(T), +} + +// Triggers correctly even when interleaved with other attributes. +/// A multiline +#[derive(Debug)] +/// doc comment: +/// only the last line triggers the lint +//~^ doc_paragraphs_missing_punctuation +enum Exceptions { + /// Question marks are fine? + QuestionMark, + /// Exclamation marks are fine! + ExclamationMark, + /// Ellipses are ok too… + Ellipsis, + /// HTML content is however not checked: + /// Raw HTML is allowed as well + RawHtml, + /// The raw HTML exception actually does the right thing to autolinks: + /// + //~^ doc_paragraphs_missing_punctuation + MarkdownAutolink, + /// This table introduction ends with a colon: + /// + /// | Exception | Note | + /// | -------------- | ----- | + /// | Markdown table | A-ok | + MarkdownTable, + /// Here is a snippet + //~^ doc_paragraphs_missing_punctuation + /// + /// ``` + /// // Code blocks are no issues. + /// ``` + CodeBlock, +} + +// Check the lint can be expected on a whole enum at once. +#[expect(clippy::doc_paragraphs_missing_punctuation)] +enum Char { + /// U+0000 + Null, + /// U+0001 + StartOfHeading, +} + +// Check the lint can be expected on a single variant without affecting others. +enum Char2 { + #[expect(clippy::doc_paragraphs_missing_punctuation)] + /// U+0000 + Null, + /// U+0001 + //~^ doc_paragraphs_missing_punctuation + StartOfHeading, +} + +mod module { + //! Works on + //! inner attributes too + //~^ doc_paragraphs_missing_punctuation +} + +enum Trailers { + /// Sometimes the last sentence ends with parentheses (and that's ok). + ParensPassing, + /// (Sometimes the last sentence is in parentheses.) + SentenceInParensPassing, + /// **Sometimes the last sentence is in bold, and that's ok.** + DoubleStarPassing, + /// **But sometimes it is missing a period** + //~^ doc_paragraphs_missing_punctuation + DoubleStarFailing, + /// _Sometimes the last sentence is in italics, and that's ok._ + UnderscorePassing, + /// _But sometimes it is missing a period_ + //~^ doc_paragraphs_missing_punctuation + UnderscoreFailing, + /// This comment ends with "a quote." + AmericanStyleQuotePassing, + /// This comment ends with "a quote". + BritishStyleQuotePassing, +} + +/// Doc comments can end with an [inline link](#anchor) +//~^ doc_paragraphs_missing_punctuation +struct InlineLink; + +/// Some doc comments contain [link reference definitions][spec] +//~^ doc_paragraphs_missing_punctuation +/// +/// [spec]: https://spec.commonmark.org/0.31.2/#link-reference-definitions +struct LinkRefDefinition; + +// List items do not always need to end with a period. +enum UnorderedLists { + /// This list has an introductory sentence: + /// + /// - A list item + Dash, + /// + A list item + Plus, + /// * A list item + Star, +} + +enum OrderedLists { + /// 1. A list item + Dot, + /// 42) A list item + Paren, +} + +/// Doc comments with trailing blank lines are supported +//~^ doc_paragraphs_missing_punctuation +/// +struct TrailingBlankLine; + +/// This doc comment has multiple paragraph. +/// This first paragraph is missing punctuation +//~^ doc_paragraphs_missing_punctuation +/// +/// The second one as well +/// And it has multiple sentences +//~^ doc_paragraphs_missing_punctuation +/// +/// Same for this third and last one +//~^ doc_paragraphs_missing_punctuation +struct MultiParagraphDocComment; + +/// ``` +struct IncompleteBlockCode; + +/// This ends with a code `span` +//~^ doc_paragraphs_missing_punctuation +struct CodeSpan; + +#[expect(clippy::empty_docs)] +/// +struct EmptyDocComment; + +/** + * Block doc comments work + * + */ +//~^^^ doc_paragraphs_missing_punctuation +struct BlockDocComment; + +/// Sometimes a doc attribute is used for concatenation +/// ``` +#[doc = ""] +/// ``` +struct DocAttribute; diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr b/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr new file mode 100644 index 000000000000..49aa4e8aeb88 --- /dev/null +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr @@ -0,0 +1,113 @@ +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:5:86 + | +LL | /// Returns the Answer to the Ultimate Question of Life, the Universe, and Everything + | ^ help: end the paragraph with some punctuation: `.` + | + = note: `-D clippy::doc-paragraphs-missing-punctuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_paragraphs_missing_punctuation)]` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:11:22 + | +LL | /// The `Option` type + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:16:17 + | +LL | /// No value + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:27:41 + | +LL | /// only the last line triggers the lint + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:40:56 + | +LL | /// + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:49:26 + | +LL | /// Here is a snippet + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:72:15 + | +LL | /// U+0001 + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:79:29 + | +LL | //! inner attributes too + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:90:47 + | +LL | /// **But sometimes it is missing a period** + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:95:46 + | +LL | /// _But sometimes it is missing a period_ + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:104:56 + | +LL | /// Doc comments can end with an [inline link](#anchor) + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:108:65 + | +LL | /// Some doc comments contain [link reference definitions][spec] + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:133:57 + | +LL | /// Doc comments with trailing blank lines are supported + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:139:48 + | +LL | /// This first paragraph is missing punctuation + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:143:34 + | +LL | /// And it has multiple sentences + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:146:37 + | +LL | /// Same for this third and last one + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:153:33 + | +LL | /// This ends with a code `span` + | ^ help: end the paragraph with some punctuation: `.` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:162:27 + | +LL | * Block doc comments work + | ^ help: end the paragraph with some punctuation: `.` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs b/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs new file mode 100644 index 000000000000..3873f1d1edcf --- /dev/null +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs @@ -0,0 +1,13 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] +#![warn(clippy::doc_paragraphs_missing_punctuation)] +//@no-rustfix + +enum UnfixableTrailers { + /// Sometimes the doc comment ends with parentheses (like this) + //~^ doc_paragraphs_missing_punctuation + EndsWithParens, + /// This comment ends with "a quote" + //~^ doc_paragraphs_missing_punctuation + QuoteFailing, +} diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.stderr b/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.stderr new file mode 100644 index 000000000000..e8587eace2d6 --- /dev/null +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.stderr @@ -0,0 +1,20 @@ +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs:7:68 + | +LL | /// Sometimes the doc comment ends with parentheses (like this) + | ^ + | + = help: end the paragraph with some punctuation + = note: `-D clippy::doc-paragraphs-missing-punctuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_paragraphs_missing_punctuation)]` + +error: doc paragraphs should end with a terminal punctuation mark + --> tests/ui/doc/doc_paragraphs_missing_punctuation_unfixable.rs:10:41 + | +LL | /// This comment ends with "a quote" + | ^ + | + = help: end the paragraph with some punctuation + +error: aborting due to 2 previous errors + diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 58fbad64a78d..2cf69cf7b2fd 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,14 +1,6 @@ //@aux-build:proc_macros.rs - -#![allow( - unused_variables, - dead_code, - clippy::derive_partial_eq_without_eq, - clippy::needless_ifs -)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)] #![warn(clippy::equatable_if_let)] - -extern crate proc_macros; use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -48,7 +40,6 @@ impl PartialEq for NotStructuralEq { } } -#[inline_macros] fn main() { let a = 2; let b = 3; @@ -95,13 +86,6 @@ fn main() { //~^ equatable_if_let if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} //~^ equatable_if_let - - if "abc" == inline!("abc") { - //~^ equatable_if_let - println!("OK"); - } - - external!({ if let 2 = $a {} }); } mod issue8710 { @@ -139,3 +123,92 @@ mod issue8710 { } } } + +#[inline_macros] +fn issue14548() { + if let inline!("abc") = "abc" { + println!("OK"); + } + + let a = 2; + external!({ if let 2 = $a {} }); + + // Don't lint: `==`/`matches!` might be correct for a particular `$($font)|*`, but not in general + macro_rules! m1 { + ($($font:pat_param)|*) => { + if let $($font)|* = "from_expansion" {} + } + } + m1!("foo"); + m1!("Sans" | "Serif" | "Sans Mono"); + m1!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general + macro_rules! m2 { + ($from_root_ctxt:pat) => { + if let $from_root_ctxt = "from_expansion" {} + }; + } + m2!("foo"); + m2!("Sans" | "Serif" | "Sans Mono"); + m2!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general + macro_rules! m3 { + ($from_root_ctxt:expr) => { + if let "from_expansion" = $from_root_ctxt {} + }; + } + m3!("foo"); + m3!("foo"); + m3!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general. Don't get confused by the scrutinee coming from macro invocation + macro_rules! m4 { + ($from_root_ctxt:pat) => { + if let $from_root_ctxt = inline!("from_expansion") {} + }; + } + m4!("foo"); + m4!("Sans" | "Serif" | "Sans Mono"); + m4!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general. Don't get confused by the scrutinee coming from macro invocation + macro_rules! m5 { + ($from_root_ctxt:expr) => { + if let inline!("from_expansion") = $from_root_ctxt {} + }; + } + m5!("foo"); + m5!("foo"); + m5!(inline!("foo")); + + // Would be nice to lint: both sides are macro _invocations_, so the suggestion is correct in + // general + if let inline!("foo") = inline!("bar") {} +} + +// PartialEq is not stable in consts yet +fn issue15376() { + enum NonConstEq { + A, + B, + } + impl PartialEq for NonConstEq { + fn eq(&self, _other: &Self) -> bool { + true + } + } + + const N: NonConstEq = NonConstEq::A; + + // `impl PartialEq` is not const, suggest `matches!` + const _: u32 = if matches!(N, NonConstEq::A) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` + const _: u32 = if matches!(Some(N), Some(NonConstEq::A)) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` +} diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index cca97c76b509..94302b3dfa65 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,14 +1,6 @@ //@aux-build:proc_macros.rs - -#![allow( - unused_variables, - dead_code, - clippy::derive_partial_eq_without_eq, - clippy::needless_ifs -)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)] #![warn(clippy::equatable_if_let)] - -extern crate proc_macros; use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -48,7 +40,6 @@ impl PartialEq for NotStructuralEq { } } -#[inline_macros] fn main() { let a = 2; let b = 3; @@ -95,13 +86,6 @@ fn main() { //~^ equatable_if_let if let NoPartialEqStruct { a: 2, b: false } = h {} //~^ equatable_if_let - - if let inline!("abc") = "abc" { - //~^ equatable_if_let - println!("OK"); - } - - external!({ if let 2 = $a {} }); } mod issue8710 { @@ -139,3 +123,92 @@ mod issue8710 { } } } + +#[inline_macros] +fn issue14548() { + if let inline!("abc") = "abc" { + println!("OK"); + } + + let a = 2; + external!({ if let 2 = $a {} }); + + // Don't lint: `==`/`matches!` might be correct for a particular `$($font)|*`, but not in general + macro_rules! m1 { + ($($font:pat_param)|*) => { + if let $($font)|* = "from_expansion" {} + } + } + m1!("foo"); + m1!("Sans" | "Serif" | "Sans Mono"); + m1!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general + macro_rules! m2 { + ($from_root_ctxt:pat) => { + if let $from_root_ctxt = "from_expansion" {} + }; + } + m2!("foo"); + m2!("Sans" | "Serif" | "Sans Mono"); + m2!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general + macro_rules! m3 { + ($from_root_ctxt:expr) => { + if let "from_expansion" = $from_root_ctxt {} + }; + } + m3!("foo"); + m3!("foo"); + m3!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general. Don't get confused by the scrutinee coming from macro invocation + macro_rules! m4 { + ($from_root_ctxt:pat) => { + if let $from_root_ctxt = inline!("from_expansion") {} + }; + } + m4!("foo"); + m4!("Sans" | "Serif" | "Sans Mono"); + m4!(inline!("foo")); + + // Don't lint: the suggestion might be correct for a particular `$from_root_ctxt`, but not in + // general. Don't get confused by the scrutinee coming from macro invocation + macro_rules! m5 { + ($from_root_ctxt:expr) => { + if let inline!("from_expansion") = $from_root_ctxt {} + }; + } + m5!("foo"); + m5!("foo"); + m5!(inline!("foo")); + + // Would be nice to lint: both sides are macro _invocations_, so the suggestion is correct in + // general + if let inline!("foo") = inline!("bar") {} +} + +// PartialEq is not stable in consts yet +fn issue15376() { + enum NonConstEq { + A, + B, + } + impl PartialEq for NonConstEq { + fn eq(&self, _other: &Self) -> bool { + true + } + } + + const N: NonConstEq = NonConstEq::A; + + // `impl PartialEq` is not const, suggest `matches!` + const _: u32 = if let NonConstEq::A = N { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` + const _: u32 = if let Some(NonConstEq::A) = Some(N) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` +} diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index dd1832ad68b2..8cc78daa2535 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 - --> tests/ui/equatable_if_let.rs:64:8 + --> tests/ui/equatable_if_let.rs:55:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -8,100 +8,106 @@ LL | if let 2 = a {} = help: to override `-D warnings` add `#[allow(clippy::equatable_if_let)]` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:66:8 + --> tests/ui/equatable_if_let.rs:57: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 - --> tests/ui/equatable_if_let.rs:68:8 + --> tests/ui/equatable_if_let.rs:59:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:70:8 + --> tests/ui/equatable_if_let.rs:61: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 - --> tests/ui/equatable_if_let.rs:72:8 + --> tests/ui/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 - --> tests/ui/equatable_if_let.rs:74:8 + --> tests/ui/equatable_if_let.rs:65: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 - --> tests/ui/equatable_if_let.rs:76:8 + --> tests/ui/equatable_if_let.rs:67:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:78:8 + --> tests/ui/equatable_if_let.rs:69: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!` - --> tests/ui/equatable_if_let.rs:88:8 + --> tests/ui/equatable_if_let.rs:79:8 | LL | if let NotPartialEq::A = f {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:90:8 + --> tests/ui/equatable_if_let.rs:81:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:92:8 + --> tests/ui/equatable_if_let.rs:83: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 - --> tests/ui/equatable_if_let.rs:94:8 + --> tests/ui/equatable_if_let.rs:85:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:96:8 + --> tests/ui/equatable_if_let.rs:87: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 - --> tests/ui/equatable_if_let.rs:99:8 - | -LL | if let inline!("abc") = "abc" { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` - error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:109:12 + --> tests/ui/equatable_if_let.rs:93:12 | LL | if let Some('i') = cs.iter().next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:117:12 + --> tests/ui/equatable_if_let.rs:101:12 | LL | if let Some(1) = cs.iter().next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:135:12 + --> tests/ui/equatable_if_let.rs:119:12 | LL | if let Some(MyEnum::B) = get_enum() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` -error: aborting due to 17 previous errors +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:210:23 + | +LL | const _: u32 = if let NonConstEq::A = N { 0 } else { 1 }; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(N, NonConstEq::A)` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:212:23 + | +LL | const _: u32 = if let Some(NonConstEq::A) = Some(N) { 0 } else { 1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(N), Some(NonConstEq::A))` + +error: aborting due to 18 previous errors diff --git a/tests/ui/equatable_if_let_const_cmp.fixed b/tests/ui/equatable_if_let_const_cmp.fixed new file mode 100644 index 000000000000..51dab25ed6d8 --- /dev/null +++ b/tests/ui/equatable_if_let_const_cmp.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::equatable_if_let)] +#![allow(clippy::eq_op)] +#![feature(const_trait_impl, const_cmp)] + +fn issue15376() { + enum ConstEq { + A, + B, + } + impl const PartialEq for ConstEq { + fn eq(&self, _other: &Self) -> bool { + true + } + } + + const C: ConstEq = ConstEq::A; + + // `impl PartialEq` is const... but we still suggest `matches!` for now + // TODO: detect this and suggest `=` + const _: u32 = if matches!(C, ConstEq::A) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` + const _: u32 = if matches!(Some(C), Some(ConstEq::A)) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` +} diff --git a/tests/ui/equatable_if_let_const_cmp.rs b/tests/ui/equatable_if_let_const_cmp.rs new file mode 100644 index 000000000000..b402e05c53de --- /dev/null +++ b/tests/ui/equatable_if_let_const_cmp.rs @@ -0,0 +1,24 @@ +#![warn(clippy::equatable_if_let)] +#![allow(clippy::eq_op)] +#![feature(const_trait_impl, const_cmp)] + +fn issue15376() { + enum ConstEq { + A, + B, + } + impl const PartialEq for ConstEq { + fn eq(&self, _other: &Self) -> bool { + true + } + } + + const C: ConstEq = ConstEq::A; + + // `impl PartialEq` is const... but we still suggest `matches!` for now + // TODO: detect this and suggest `=` + const _: u32 = if let ConstEq::A = C { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` + const _: u32 = if let Some(ConstEq::A) = Some(C) { 0 } else { 1 }; + //~^ ERROR: this pattern matching can be expressed using `matches!` +} diff --git a/tests/ui/equatable_if_let_const_cmp.stderr b/tests/ui/equatable_if_let_const_cmp.stderr new file mode 100644 index 000000000000..ec72e42d6430 --- /dev/null +++ b/tests/ui/equatable_if_let_const_cmp.stderr @@ -0,0 +1,17 @@ +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let_const_cmp.rs:20:23 + | +LL | const _: u32 = if let ConstEq::A = C { 0 } else { 1 }; + | ^^^^^^^^^^^^^^^^^^ help: try: `matches!(C, ConstEq::A)` + | + = note: `-D clippy::equatable-if-let` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::equatable_if_let)]` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let_const_cmp.rs:22:23 + | +LL | const _: u32 = if let Some(ConstEq::A) = Some(C) { 0 } else { 1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(C), Some(ConstEq::A))` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 97e8e0bafe4f..6c29630dc3a5 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -156,3 +156,116 @@ fn main() { let _ = &&mut **&mut x; //~ explicit_deref_methods let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods } + +mod issue_15392 { + use std::ops::{Deref, DerefMut}; + + struct Wrapper(String); + + impl Deref for Wrapper { + type Target = str; + fn deref(&self) -> &Self::Target { + // forwarding is ok + let res = Deref::deref(&self.0); + // we let `deref_mut` pass as well + let _ = DerefMut::deref_mut(&mut String::new()); + res + } + } + + impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + // forwarding is ok + let res = DerefMut::deref_mut(&mut self.0); + // we let `deref` pass as well + let _ = Deref::deref(&String::new()); + res + } + } + + struct A(String); + struct AA(String); + struct AB(String); + + impl Deref for A { + type Target = str; + + fn deref(&self) -> &Self::Target { + // in a top-level `Deref` impl, ok + let _ = self.0.deref(); + // in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + + #[allow(non_local_definitions)] + impl Deref for AA { + type Target = str; + fn deref(&self) -> &Self::Target { + // in a nested `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // in a nested `Deref` impl, ok + self.0.deref() + } + } + + // still in a top-level `Deref` impl, ok + let _ = self.0.deref(); + // still in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + + #[allow(non_local_definitions)] + impl DerefMut for AA { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // in a top-level `DerefMut` impl, ok + self.0.deref_mut() + } + } + + // still in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // still in a top-level `Deref` impl, ok + self.0.deref() + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // in a top-level `DerefMut` impl, ok + let _ = self.0.deref_mut(); + + #[allow(non_local_definitions)] + impl Deref for AB { + type Target = str; + fn deref(&self) -> &Self::Target { + // in a nested `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // in a nested `Deref` impl, ok + Deref::deref(&self.0) + } + } + + // still in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // still in a top-level `DerefMut` impl, ok + let _ = self.0.deref_mut(); + + #[allow(non_local_definitions)] + impl DerefMut for AB { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a nested `DerefMut` impl, acceptable + self.0.deref(); + // in a nested `DerefMut` impl, ok + self.0.deref_mut() + } + } + + // still in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // still in a top-level `DerefMut` impl, ok + self.0.deref_mut() + } + } +} diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index b689649d49dd..f6309cd404b8 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -156,3 +156,116 @@ fn main() { let _ = &DerefMut::deref_mut(&mut x); //~ explicit_deref_methods let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); //~ explicit_deref_methods } + +mod issue_15392 { + use std::ops::{Deref, DerefMut}; + + struct Wrapper(String); + + impl Deref for Wrapper { + type Target = str; + fn deref(&self) -> &Self::Target { + // forwarding is ok + let res = Deref::deref(&self.0); + // we let `deref_mut` pass as well + let _ = DerefMut::deref_mut(&mut String::new()); + res + } + } + + impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + // forwarding is ok + let res = DerefMut::deref_mut(&mut self.0); + // we let `deref` pass as well + let _ = Deref::deref(&String::new()); + res + } + } + + struct A(String); + struct AA(String); + struct AB(String); + + impl Deref for A { + type Target = str; + + fn deref(&self) -> &Self::Target { + // in a top-level `Deref` impl, ok + let _ = self.0.deref(); + // in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + + #[allow(non_local_definitions)] + impl Deref for AA { + type Target = str; + fn deref(&self) -> &Self::Target { + // in a nested `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // in a nested `Deref` impl, ok + self.0.deref() + } + } + + // still in a top-level `Deref` impl, ok + let _ = self.0.deref(); + // still in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + + #[allow(non_local_definitions)] + impl DerefMut for AA { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // in a top-level `DerefMut` impl, ok + self.0.deref_mut() + } + } + + // still in a top-level `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // still in a top-level `Deref` impl, ok + self.0.deref() + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // in a top-level `DerefMut` impl, ok + let _ = self.0.deref_mut(); + + #[allow(non_local_definitions)] + impl Deref for AB { + type Target = str; + fn deref(&self) -> &Self::Target { + // in a nested `Deref` impl, acceptable + let _ = String::new().deref_mut(); + // in a nested `Deref` impl, ok + Deref::deref(&self.0) + } + } + + // still in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // still in a top-level `DerefMut` impl, ok + let _ = self.0.deref_mut(); + + #[allow(non_local_definitions)] + impl DerefMut for AB { + fn deref_mut(&mut self) -> &mut Self::Target { + // in a nested `DerefMut` impl, acceptable + self.0.deref(); + // in a nested `DerefMut` impl, ok + self.0.deref_mut() + } + } + + // still in a top-level `DerefMut` impl, acceptable + let _ = self.0.deref(); + // still in a top-level `DerefMut` impl, ok + self.0.deref_mut() + } + } +} diff --git a/tests/ui/implicit_hasher.fixed b/tests/ui/implicit_hasher.fixed index bea5b9afc43a..882a545963b4 100644 --- a/tests/ui/implicit_hasher.fixed +++ b/tests/ui/implicit_hasher.fixed @@ -109,3 +109,27 @@ pub async fn election_vote(_data: HashMap { + $num * 10 + }; + } + + impl Foo for HashMap { + //~^ implicit_hasher + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(times_ten!(5), Default::default())) + } + } + + impl Foo for HashSet { + //~^ implicit_hasher + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(times_ten!(5), Default::default())) + } + } +} diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index afdf4da61650..186f9e9978e5 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -109,3 +109,27 @@ pub async fn election_vote(_data: HashMap) {} //~^ implicit_hasher fn main() {} + +mod issue16128 { + use super::*; + + macro_rules! times_ten { + ($num:expr) => { + $num * 10 + }; + } + + impl Foo for HashMap { + //~^ implicit_hasher + fn make() -> (Self, Self) { + (HashMap::new(), HashMap::with_capacity(times_ten!(5))) + } + } + + impl Foo for HashSet { + //~^ implicit_hasher + fn make() -> (Self, Self) { + (HashSet::new(), HashSet::with_capacity(times_ten!(5))) + } + } +} diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index 6735998ed654..326115c8e9a3 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -122,5 +122,33 @@ help: add a type parameter for `BuildHasher` LL | pub async fn election_vote(_data: HashMap) {} | +++++++++++++++++++++++++++++ +++ -error: aborting due to 9 previous errors +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:122:40 + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl Foo for HashMap { +LL | +LL | fn make() -> (Self, Self) { +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(times_ten!(5), Default::default())) + | + +error: impl for `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:129:37 + | +LL | impl Foo for HashSet { + | ^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl Foo for HashSet { +LL | +LL | fn make() -> (Self, Self) { +LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(times_ten!(5), Default::default())) + | + +error: aborting due to 11 previous errors diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs deleted file mode 100644 index 705de959cb7d..000000000000 --- a/tests/ui/missing_doc.rs +++ /dev/null @@ -1,148 +0,0 @@ -//@needs-asm-support -//@aux-build: proc_macros.rs -//@aux-build: proc_macro_attr.rs - -#![warn(clippy::missing_docs_in_private_items)] -// When denying at the crate level, be sure to not get random warnings from the -// injected intrinsics by the compiler. -#![allow(dead_code)] -//! Some garbage docs for the crate here -#![doc = "More garbage"] - -#[macro_use] -extern crate proc_macro_attr; -extern crate proc_macros; - -use proc_macros::with_span; -use std::arch::global_asm; - -type Typedef = String; -//~^ missing_docs_in_private_items -pub type PubTypedef = String; - -mod module_no_dox {} -//~^ missing_docs_in_private_items -pub mod pub_module_no_dox {} - -/// dox -pub fn foo() {} -pub fn foo2() {} -fn foo3() {} -//~^ missing_docs_in_private_items -#[allow(clippy::missing_docs_in_private_items)] -pub fn foo4() {} - -// It sure is nice if doc(hidden) implies allow(missing_docs), and that it -// applies recursively -#[doc(hidden)] -mod a { - pub fn baz() {} - pub mod b { - pub fn baz() {} - } -} - -enum Baz { - //~^ missing_docs_in_private_items - BazA { a: isize, b: isize }, - //~^ missing_docs_in_private_items - //~| missing_docs_in_private_items - //~| missing_docs_in_private_items - BarB, - //~^ missing_docs_in_private_items -} - -pub enum PubBaz { - PubBazA { a: isize }, -} - -/// dox -pub enum PubBaz2 { - /// dox - PubBaz2A { - /// dox - a: isize, - }, -} - -#[allow(clippy::missing_docs_in_private_items)] -pub enum PubBaz3 { - PubBaz3A { b: isize }, -} - -#[doc(hidden)] -pub fn baz() {} - -const FOO: u32 = 0; -//~^ missing_docs_in_private_items -/// dox -pub const FOO1: u32 = 0; -#[allow(clippy::missing_docs_in_private_items)] -pub const FOO2: u32 = 0; -#[doc(hidden)] -pub const FOO3: u32 = 0; -pub const FOO4: u32 = 0; - -static BAR: u32 = 0; -//~^ missing_docs_in_private_items -/// dox -pub static BAR1: u32 = 0; -#[allow(clippy::missing_docs_in_private_items)] -pub static BAR2: u32 = 0; -#[doc(hidden)] -pub static BAR3: u32 = 0; -pub static BAR4: u32 = 0; - -mod internal_impl { - //~^ missing_docs_in_private_items - /// dox - pub fn documented() {} - pub fn undocumented1() {} - pub fn undocumented2() {} - fn undocumented3() {} - //~^ missing_docs_in_private_items - /// dox - pub mod globbed { - /// dox - pub fn also_documented() {} - pub fn also_undocumented1() {} - fn also_undocumented2() {} - //~^ missing_docs_in_private_items - } -} -/// dox -pub mod public_interface { - pub use crate::internal_impl::globbed::*; - pub use crate::internal_impl::{documented as foo, documented, undocumented1 as bar, undocumented2}; -} - -fn main() {} - -// Ensure global asm doesn't require documentation. -global_asm! { "" } - -// Don't lint proc macro output with an unexpected span. -with_span!(span pub struct FooPm { pub field: u32}); -with_span!(span pub struct FooPm2;); -with_span!(span pub enum FooPm3 { A, B(u32), C { field: u32 }}); -with_span!(span pub fn foo_pm() {}); -with_span!(span pub static FOO_PM: u32 = 0;); -with_span!(span pub const FOO2_PM: u32 = 0;); - -// Don't lint unnamed constants -const _: () = (); - -fn issue13298() { - //~^ missing_docs_in_private_items - // Rustdoc doesn't generate documentation for items within other items like fns or consts - const MSG: &str = "Hello, world!"; -} - -// issue #12197 -// Undocumented field originated inside of spanned proc-macro attribute -/// Some dox for struct. -#[rewrite_struct] -pub struct Test { - /// Dox - a: u8, -} diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr deleted file mode 100644 index 63e440b82d14..000000000000 --- a/tests/ui/missing_doc.stderr +++ /dev/null @@ -1,102 +0,0 @@ -error: missing documentation for a type alias - --> tests/ui/missing_doc.rs:19:1 - | -LL | type Typedef = String; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` - -error: missing documentation for a module - --> tests/ui/missing_doc.rs:23:1 - | -LL | mod module_no_dox {} - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> tests/ui/missing_doc.rs:30:1 - | -LL | fn foo3() {} - | ^^^^^^^^^^^^ - -error: missing documentation for an enum - --> tests/ui/missing_doc.rs:45:1 - | -LL | / enum Baz { -LL | | -LL | | BazA { a: isize, b: isize }, -... | -LL | | } - | |_^ - -error: missing documentation for a variant - --> tests/ui/missing_doc.rs:47:5 - | -LL | BazA { a: isize, b: isize }, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:47:12 - | -LL | BazA { a: isize, b: isize }, - | ^^^^^^^^ - -error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:47:22 - | -LL | BazA { a: isize, b: isize }, - | ^^^^^^^^ - -error: missing documentation for a variant - --> tests/ui/missing_doc.rs:51:5 - | -LL | BarB, - | ^^^^ - -error: missing documentation for a constant - --> tests/ui/missing_doc.rs:76:1 - | -LL | const FOO: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a static - --> tests/ui/missing_doc.rs:86:1 - | -LL | static BAR: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a module - --> tests/ui/missing_doc.rs:96:1 - | -LL | / mod internal_impl { -LL | | -LL | | /// dox -LL | | pub fn documented() {} -... | -LL | | } - | |_^ - -error: missing documentation for a function - --> tests/ui/missing_doc.rs:102:5 - | -LL | fn undocumented3() {} - | ^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> tests/ui/missing_doc.rs:109:9 - | -LL | fn also_undocumented2() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> tests/ui/missing_doc.rs:135:1 - | -LL | / fn issue13298() { -LL | | -LL | | // Rustdoc doesn't generate documentation for items within other items like fns or consts -LL | | const MSG: &str = "Hello, world!"; -LL | | } - | |_^ - -error: aborting due to 14 previous errors - diff --git a/tests/ui/missing_doc_crate.rs b/tests/ui/missing_doc_crate.rs deleted file mode 100644 index e6e783a2bb40..000000000000 --- a/tests/ui/missing_doc_crate.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ check-pass - -#![warn(clippy::missing_docs_in_private_items)] -#![allow(clippy::doc_include_without_cfg)] -#![doc = include_str!("../../README.md")] - -fn main() {} diff --git a/tests/ui/missing_doc_crate_missing.rs b/tests/ui/missing_doc_crate_missing.rs deleted file mode 100644 index f55d8b67cb84..000000000000 --- a/tests/ui/missing_doc_crate_missing.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![warn(clippy::missing_docs_in_private_items)] -//~^ missing_docs_in_private_items - -fn main() {} diff --git a/tests/ui/missing_doc_crate_missing.stderr b/tests/ui/missing_doc_crate_missing.stderr deleted file mode 100644 index d6a4342c5031..000000000000 --- a/tests/ui/missing_doc_crate_missing.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: missing documentation for the crate - --> tests/ui/missing_doc_crate_missing.rs:1:1 - | -LL | / #![warn(clippy::missing_docs_in_private_items)] -... | -LL | | fn main() {} - | |____________^ - | - = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/missing_doc_impl.rs b/tests/ui/missing_doc_impl.rs deleted file mode 100644 index 034ce31dfe76..000000000000 --- a/tests/ui/missing_doc_impl.rs +++ /dev/null @@ -1,114 +0,0 @@ -//@aux-build: proc_macros.rs - -#![warn(clippy::missing_docs_in_private_items)] -#![allow(dead_code)] -#![feature(associated_type_defaults)] - -//! Some garbage docs for the crate here -#![doc = "More garbage"] - -extern crate proc_macros; -use proc_macros::with_span; - -struct Foo { - //~^ missing_docs_in_private_items - a: isize, - //~^ missing_docs_in_private_items - b: isize, - //~^ missing_docs_in_private_items -} - -pub struct PubFoo { - pub a: isize, - b: isize, - //~^ missing_docs_in_private_items -} - -#[allow(clippy::missing_docs_in_private_items)] -pub struct PubFoo2 { - pub a: isize, - pub c: isize, -} - -/// dox -pub trait A { - /// dox - fn foo(&self); - /// dox - fn foo_with_impl(&self) {} -} - -#[allow(clippy::missing_docs_in_private_items)] -trait B { - fn foo(&self); - fn foo_with_impl(&self) {} -} - -pub trait C { - fn foo(&self); - fn foo_with_impl(&self) {} -} - -#[allow(clippy::missing_docs_in_private_items)] -pub trait D { - fn dummy(&self) {} -} - -/// dox -pub trait E: Sized { - type AssociatedType; - type AssociatedTypeDef = Self; - - /// dox - type DocumentedType; - /// dox - type DocumentedTypeDef = Self; - /// dox - fn dummy(&self) {} -} - -impl Foo { - pub fn new() -> Self { - //~^ missing_docs_in_private_items - Foo { a: 0, b: 0 } - } - fn bar() {} - //~^ missing_docs_in_private_items -} - -impl PubFoo { - pub fn foo() {} - /// dox - pub fn foo1() {} - #[must_use = "yep"] - fn foo2() -> u32 { - //~^ missing_docs_in_private_items - 1 - } - #[allow(clippy::missing_docs_in_private_items)] - pub fn foo3() {} -} - -#[allow(clippy::missing_docs_in_private_items)] -trait F { - fn a(); - fn b(&self); -} - -// should need to redefine documentation for implementations of traits -impl F for Foo { - fn a() {} - fn b(&self) {} -} - -fn main() {} - -// don't lint proc macro output -with_span!(span - pub struct FooPm; - impl FooPm { - pub fn foo() {} - pub const fn bar() {} - pub const X: u32 = 0; - } -); diff --git a/tests/ui/missing_doc_impl.stderr b/tests/ui/missing_doc_impl.stderr deleted file mode 100644 index 999ff06f593e..000000000000 --- a/tests/ui/missing_doc_impl.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: missing documentation for a struct - --> tests/ui/missing_doc_impl.rs:13:1 - | -LL | / struct Foo { -LL | | -LL | | a: isize, -... | -LL | | } - | |_^ - | - = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` - -error: missing documentation for a struct field - --> tests/ui/missing_doc_impl.rs:15:5 - | -LL | a: isize, - | ^^^^^^^^ - -error: missing documentation for a struct field - --> tests/ui/missing_doc_impl.rs:17:5 - | -LL | b: isize, - | ^^^^^^^^ - -error: missing documentation for a struct field - --> tests/ui/missing_doc_impl.rs:23:5 - | -LL | b: isize, - | ^^^^^^^^ - -error: missing documentation for an associated function - --> tests/ui/missing_doc_impl.rs:71:5 - | -LL | / pub fn new() -> Self { -LL | | -LL | | Foo { a: 0, b: 0 } -LL | | } - | |_____^ - -error: missing documentation for an associated function - --> tests/ui/missing_doc_impl.rs:75:5 - | -LL | fn bar() {} - | ^^^^^^^^^^^ - -error: missing documentation for an associated function - --> tests/ui/missing_doc_impl.rs:84:5 - | -LL | / fn foo2() -> u32 { -LL | | -LL | | 1 -LL | | } - | |_____^ - -error: aborting due to 7 previous errors - diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 132673d5164a..c1512ba3e269 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -105,6 +105,14 @@ fn correct3() { } } +fn with_adjustment(f: &unsafe fn()) { + unsafe { + //~^ multiple_unsafe_ops_per_block + f(); + f(); + } +} + fn issue10064() { unsafe fn read_char_bad(ptr: *const u8) -> char { unsafe { char::from_u32_unchecked(*ptr.cast::()) } @@ -209,4 +217,99 @@ async fn issue13879() { } } +fn issue16076() { + #[derive(Clone, Copy)] + union U { + i: u32, + f: f32, + } + + let u = U { i: 0 }; + + // Taking a raw pointer to a place is safe since Rust 1.92 + unsafe { + _ = &raw const u.i; + _ = &raw const u.i; + } + + // Taking a reference to a union field is not safe + unsafe { + //~^ multiple_unsafe_ops_per_block + _ = &u.i; + _ = &u.i; + } + + // Check that we still check and lint the prefix of the raw pointer to a field access + #[expect(clippy::deref_addrof)] + unsafe { + //~^ multiple_unsafe_ops_per_block + _ = &raw const (*&raw const u).i; + _ = &raw const (*&raw const u).i; + } + + union V { + u: U, + } + + // Taking a raw pointer to a union field of an union field (etc.) is safe + let v = V { u }; + unsafe { + _ = &raw const v.u.i; + _ = &raw const v.u.i; + } + + // Check that unions in structs work properly as well + struct T { + u: U, + } + let t = T { u }; + unsafe { + _ = &raw const t.u.i; + _ = &raw const t.u.i; + } + + // As well as structs in unions + #[derive(Clone, Copy)] + struct X { + i: i32, + } + union Z { + x: X, + } + let z = Z { x: X { i: 0 } }; + unsafe { + _ = &raw const z.x.i; + _ = &raw const z.x.i; + } + + // If a field needs to be adjusted then it is accessed + struct S { + i: i32, + } + union W<'a> { + s: &'a S, + } + let s = S { i: 0 }; + let w = W { s: &s }; + unsafe { + //~^ multiple_unsafe_ops_per_block + _ = &raw const w.s.i; + _ = &raw const w.s.i; + } +} + +fn check_closures() { + unsafe fn apply(f: impl Fn()) { + todo!() + } + unsafe fn f(_x: i32) { + todo!() + } + + unsafe { + //~^ multiple_unsafe_ops_per_block + apply(|| f(0)); + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 922a464c6b6e..63f7742b734b 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -113,24 +113,45 @@ LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:109:5 + | +LL | / unsafe { +LL | | +LL | | f(); +LL | | f(); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:111:9 + | +LL | f(); + | ^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:112:9 + | +LL | f(); + | ^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:118:9 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18 + --> tests/ui/multiple_unsafe_ops_per_block.rs:118:18 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43 + --> tests/ui/multiple_unsafe_ops_per_block.rs:118:43 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:139:9 | LL | / unsafe { LL | | @@ -140,18 +161,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:141:13 | LL | x(); | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:142:13 | LL | x(); | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:151:13 | LL | / unsafe { LL | | @@ -161,18 +182,18 @@ LL | | } | |_____________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:153:17 | LL | T::X(); | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:154:17 | LL | T::X(); | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:162:9 | LL | / unsafe { LL | | @@ -182,18 +203,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:164:13 | LL | x.0(); | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:165:13 | LL | x.0(); | ^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:192:5 | LL | / unsafe { LL | | @@ -204,18 +225,18 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:194:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:195:9 | LL | STATIC += 1; | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:207:5 | LL | / unsafe { LL | | @@ -225,18 +246,18 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:209:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:210:9 | LL | foo_unchecked().await; | ^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:214:5 | LL | / unsafe { LL | | @@ -245,15 +266,98 @@ LL | | } | |_____^ | note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:216:9 | LL | Some(foo_unchecked()).unwrap_unchecked().await; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:216:14 | LL | Some(foo_unchecked()).unwrap_unchecked().await; | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:236:5 + | +LL | / unsafe { +LL | | +LL | | _ = &u.i; +LL | | _ = &u.i; +LL | | } + | |_____^ + | +note: union field access occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:238:14 + | +LL | _ = &u.i; + | ^^^ +note: union field access occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:239:14 + | +LL | _ = &u.i; + | ^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:244:5 + | +LL | / unsafe { +LL | | +LL | | _ = &raw const (*&raw const u).i; +LL | | _ = &raw const (*&raw const u).i; +LL | | } + | |_____^ + | +note: raw pointer dereference occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:246:24 + | +LL | _ = &raw const (*&raw const u).i; + | ^^^^^^^^^^^^^^^ +note: raw pointer dereference occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:247:24 + | +LL | _ = &raw const (*&raw const u).i; + | ^^^^^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:294:5 + | +LL | / unsafe { +LL | | +LL | | _ = &raw const w.s.i; +LL | | _ = &raw const w.s.i; +LL | | } + | |_____^ + | +note: union field access occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:296:24 + | +LL | _ = &raw const w.s.i; + | ^^^ +note: union field access occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:297:24 + | +LL | _ = &raw const w.s.i; + | ^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:309:5 + | +LL | / unsafe { +LL | | +LL | | apply(|| f(0)); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:311:9 + | +LL | apply(|| f(0)); + | ^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:311:18 + | +LL | apply(|| f(0)); + | ^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index a54a4ce13d53..08903ef7fdda 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -166,3 +166,32 @@ fn issue13902() { //~^ redundant_pattern_matching } } + +fn issue16045() { + fn f() -> Result<(), ()> { + let x = Ok::<_, ()>(Some(123)); + if x?.is_some() { + //~^ redundant_pattern_matching + } + + Ok(()) + } + + async fn g() { + struct F { + x: Option, + } + + impl Future for F { + type Output = Option; + + fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> std::task::Poll { + std::task::Poll::Ready(self.x) + } + } + let x = F { x: Some(123) }; + if x.await.is_some() { + //~^ redundant_pattern_matching + } + } +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 7252fce8cce6..95eff3f9ebf9 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -202,3 +202,32 @@ fn issue13902() { //~^ redundant_pattern_matching } } + +fn issue16045() { + fn f() -> Result<(), ()> { + let x = Ok::<_, ()>(Some(123)); + if let Some(_) = x? { + //~^ redundant_pattern_matching + } + + Ok(()) + } + + async fn g() { + struct F { + x: Option, + } + + impl Future for F { + type Output = Option; + + fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> std::task::Poll { + std::task::Poll::Ready(self.x) + } + } + let x = F { x: Some(123) }; + if let Some(_) = x.await { + //~^ redundant_pattern_matching + } + } +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index e5a6598898aa..6fd0c5a6f859 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -224,5 +224,17 @@ error: redundant pattern matching, consider using `is_none()` LL | let _ = matches!(*p, None); | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` -error: aborting due to 31 previous errors +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:209:16 + | +LL | if let Some(_) = x? { + | -------^^^^^^^----- help: try: `if x?.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:229:16 + | +LL | if let Some(_) = x.await { + | -------^^^^^^^---------- help: try: `if x.await.is_some()` + +error: aborting due to 33 previous errors diff --git a/tests/ui/sliced_string_as_bytes.fixed b/tests/ui/sliced_string_as_bytes.fixed index 16c0daff78fd..b5576188b83f 100644 --- a/tests/ui/sliced_string_as_bytes.fixed +++ b/tests/ui/sliced_string_as_bytes.fixed @@ -32,6 +32,12 @@ fn main() { let bytes = &"consectetur adipiscing".as_bytes()[..=5]; //~^ sliced_string_as_bytes + // this lint is a perf lint meant to catch utf-8 alignment checks. + // while the slicing here *is* redundant, it's more like a needless borrow, and shouldn't affect + // perf + let bytes = s[..].as_bytes(); + let bytes = string[..].as_bytes(); + let f = Foo; let bytes = f[0..4].as_bytes(); } diff --git a/tests/ui/sliced_string_as_bytes.rs b/tests/ui/sliced_string_as_bytes.rs index 67985ae5b984..58b8d9290294 100644 --- a/tests/ui/sliced_string_as_bytes.rs +++ b/tests/ui/sliced_string_as_bytes.rs @@ -32,6 +32,12 @@ fn main() { let bytes = "consectetur adipiscing"[..=5].as_bytes(); //~^ sliced_string_as_bytes + // this lint is a perf lint meant to catch utf-8 alignment checks. + // while the slicing here *is* redundant, it's more like a needless borrow, and shouldn't affect + // perf + let bytes = s[..].as_bytes(); + let bytes = string[..].as_bytes(); + let f = Foo; let bytes = f[0..4].as_bytes(); } diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index e7099104f942..afb79deac20f 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -128,4 +128,17 @@ fn bytes_to_str(mb: &mut [u8]) { //~^ transmute_bytes_to_str } +fn issue16104() { + let b = vec![1_u8, 2_u8]; + macro_rules! take_ref { + ($x:expr) => { + $x.as_slice() + }; + } + unsafe { + let _: &str = std::mem::transmute(take_ref!(b)); + //~^ transmute_bytes_to_str + } +} + fn main() {} diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 9478db09481a..6f9a0b717fc9 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -106,5 +106,11 @@ error: transmute from a `&[u8]` to a `&str` LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 16 previous errors +error: transmute from a `&[u8]` to a `&str` + --> tests/ui/transmute.rs:139:23 + | +LL | let _: &str = std::mem::transmute(take_ref!(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(take_ref!(b)).unwrap()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/transmute_ref_to_ref.rs b/tests/ui/transmute_ref_to_ref.rs index 8bdf07b4a428..ed8fb8083291 100644 --- a/tests/ui/transmute_ref_to_ref.rs +++ b/tests/ui/transmute_ref_to_ref.rs @@ -18,3 +18,23 @@ fn main() { //~^ transmute_ptr_to_ptr } } + +fn issue16104(make_ptr: fn() -> *const u32) { + macro_rules! call { + ($x:expr) => { + $x() + }; + } + macro_rules! take_ref { + ($x:expr) => { + &$x + }; + } + + unsafe { + let _: *const f32 = std::mem::transmute(call!(make_ptr)); + //~^ transmute_ptr_to_ptr + let _: &f32 = std::mem::transmute(take_ref!(1u32)); + //~^ transmute_ptr_to_ptr + } +} diff --git a/tests/ui/transmute_ref_to_ref.stderr b/tests/ui/transmute_ref_to_ref.stderr index e8d659f9c5d8..1b845ef859d8 100644 --- a/tests/ui/transmute_ref_to_ref.stderr +++ b/tests/ui/transmute_ref_to_ref.stderr @@ -22,5 +22,23 @@ error: transmute from a reference to a reference LL | let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` -error: aborting due to 3 previous errors +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ref_to_ref.rs:35:29 + | +LL | let _: *const f32 = std::mem::transmute(call!(make_ptr)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast` instead + | +LL - let _: *const f32 = std::mem::transmute(call!(make_ptr)); +LL + let _: *const f32 = call!(make_ptr).cast::(); + | + +error: transmute from a reference to a reference + --> tests/ui/transmute_ref_to_ref.rs:37:23 + | +LL | let _: &f32 = std::mem::transmute(take_ref!(1u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(take_ref!(1u32) as *const u32 as *const f32)` + +error: aborting due to 5 previous errors diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr index 7a39712269cf..c129497447fc 100644 --- a/tests/ui/unchecked_time_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -1,4 +1,4 @@ -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:9:13 | LL | let _ = _first - second; @@ -7,43 +7,43 @@ LL | let _ = _first - second; = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:15:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:18:13 | LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:25:13 | LL | let _ = dur1 - dur2; | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:28:13 | LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:31:13 | LL | let _ = second - dur1; | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:35:13 | LL | let _ = 2 * dur1 - dur2; diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr index c25c112b06ce..017e5b1c7c11 100644 --- a/tests/ui/unchecked_time_subtraction_unfixable.stderr +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -1,4 +1,4 @@ -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 | LL | let _ = dur1 - dur2 - dur3; @@ -7,19 +7,19 @@ LL | let _ = dur1 - dur2 - dur3; = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` -error: unchecked subtraction between 'Duration' values +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 | LL | let _ = dur1 - dur2 - dur3; | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 | LL | let _ = instant1 - dur2 - dur3; | ^^^^^^^^^^^^^^^^^^^^^^ -error: unchecked subtraction of a 'Duration' from an 'Instant' +error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 | LL | let _ = instant1 - dur2 - dur3; diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 3c3ea5a736d4..54a7c0a8c080 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -258,6 +258,41 @@ fn issue_14828() { ().as_ref(); } +fn issue16098(exts: Vec<&str>) { + use std::borrow::Cow; + + let v: Vec> = exts.iter().map(|s| Cow::Borrowed(*s)).collect(); + //~^ useless_asref + + trait Identity { + fn id(self) -> Self + where + Self: Sized, + { + self + } + } + impl Identity for &str {} + + let v: Vec> = exts.iter().map(|s| Cow::Borrowed(s.id())).collect(); + //~^ useless_asref + + let v: Vec> = exts + .iter() + .map(|s| Cow::Borrowed(*std::convert::identity(s))) + //~^ useless_asref + .collect(); + + struct Wrapper<'a>(&'a str); + let exts_field: Vec = exts.iter().map(|s| Wrapper(s)).collect(); + let v: Vec> = exts_field.iter().map(|w| Cow::Borrowed(w.0)).collect(); + //~^ useless_asref + + let exts_index: Vec<&[&str]> = exts.iter().map(|s| std::slice::from_ref(s)).collect(); + let v: Vec> = exts_index.iter().map(|arr| Cow::Borrowed(arr[0])).collect(); + //~^ useless_asref +} + fn main() { not_ok(); ok(); diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index c173dd677152..1b9ce1d46233 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -258,6 +258,41 @@ fn issue_14828() { ().as_ref(); } +fn issue16098(exts: Vec<&str>) { + use std::borrow::Cow; + + let v: Vec> = exts.iter().map(|s| Cow::Borrowed(s.as_ref())).collect(); + //~^ useless_asref + + trait Identity { + fn id(self) -> Self + where + Self: Sized, + { + self + } + } + impl Identity for &str {} + + let v: Vec> = exts.iter().map(|s| Cow::Borrowed(s.id().as_ref())).collect(); + //~^ useless_asref + + let v: Vec> = exts + .iter() + .map(|s| Cow::Borrowed(std::convert::identity(s).as_ref())) + //~^ useless_asref + .collect(); + + struct Wrapper<'a>(&'a str); + let exts_field: Vec = exts.iter().map(|s| Wrapper(s)).collect(); + let v: Vec> = exts_field.iter().map(|w| Cow::Borrowed(w.0.as_ref())).collect(); + //~^ useless_asref + + let exts_index: Vec<&[&str]> = exts.iter().map(|s| std::slice::from_ref(s)).collect(); + let v: Vec> = exts_index.iter().map(|arr| Cow::Borrowed(arr[0].as_ref())).collect(); + //~^ useless_asref +} + fn main() { not_ok(); ok(); diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index 8255f5d9d2ab..861472b4419e 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -112,5 +112,35 @@ error: this call to `as_ref.map(...)` does nothing LL | Some(1).as_ref().map(|&x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(1).clone()` -error: aborting due to 18 previous errors +error: this call to `as_ref` does nothing + --> tests/ui/useless_asref.rs:264:66 + | +LL | let v: Vec> = exts.iter().map(|s| Cow::Borrowed(s.as_ref())).collect(); + | ^^^^^^^^^^ help: try: `*s` + +error: this call to `as_ref` does nothing + --> tests/ui/useless_asref.rs:277:66 + | +LL | let v: Vec> = exts.iter().map(|s| Cow::Borrowed(s.id().as_ref())).collect(); + | ^^^^^^^^^^^^^^^ help: try: `s.id()` + +error: this call to `as_ref` does nothing + --> tests/ui/useless_asref.rs:282:32 + | +LL | .map(|s| Cow::Borrowed(std::convert::identity(s).as_ref())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*std::convert::identity(s)` + +error: this call to `as_ref` does nothing + --> tests/ui/useless_asref.rs:288:72 + | +LL | let v: Vec> = exts_field.iter().map(|w| Cow::Borrowed(w.0.as_ref())).collect(); + | ^^^^^^^^^^^^ help: try: `w.0` + +error: this call to `as_ref` does nothing + --> tests/ui/useless_asref.rs:292:74 + | +LL | let v: Vec> = exts_index.iter().map(|arr| Cow::Borrowed(arr[0].as_ref())).collect(); + | ^^^^^^^^^^^^^^^ help: try: `arr[0]` + +error: aborting due to 23 previous errors diff --git a/tests/ui/vec.fixed b/tests/ui/useless_vec.fixed similarity index 90% rename from tests/ui/vec.fixed rename to tests/ui/useless_vec.fixed index 55742459c92c..3cea4862611d 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/useless_vec.fixed @@ -1,5 +1,4 @@ #![warn(clippy::useless_vec)] -#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)] use std::rc::Rc; @@ -39,17 +38,14 @@ fn main() { on_mut_slice(&mut [1, 2]); //~^ useless_vec - on_slice(&[1, 2]); - //~^ useless_vec - on_slice(&[1, 2]); - on_mut_slice(&mut [1, 2]); - //~^ useless_vec #[rustfmt::skip] - on_slice(&[1, 2]); - //~^ useless_vec - on_slice(&[1, 2]); - on_mut_slice(&mut [1, 2]); - //~^ useless_vec + #[allow(clippy::nonstandard_macro_braces)] // not an `expect` as it will only lint _before_ the fix + { + on_slice(&[1, 2]); + //~^ useless_vec + on_mut_slice(&mut [1, 2]); + //~^ useless_vec + }; on_slice(&[1; 2]); //~^ useless_vec @@ -75,22 +71,24 @@ fn main() { on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` - // Ok + // Ok, size of `vec` higher than `too_large_for_stack` for a in vec![1; 201] { - println!("{:?}", a); + println!("{a:?}"); } // https://github.com/rust-lang/rust-clippy/issues/2262#issuecomment-783979246 let _x: i32 = [1, 2, 3].iter().sum(); //~^ useless_vec - // Do lint - let mut x = [1, 2, 3]; - //~^ useless_vec - x.fill(123); - dbg!(x[0]); - dbg!(x.len()); - dbg!(x.iter().sum::()); + // Do lint, only used as slice + { + let mut x = [1, 2, 3]; + //~^ useless_vec + x.fill(123); + dbg!(x[0]); + dbg!(x.len()); + dbg!(x.iter().sum::()); + } let _x: &[i32] = &[1, 2, 3]; //~^ useless_vec diff --git a/tests/ui/useless_vec.rs b/tests/ui/useless_vec.rs index 880809f81d7a..2b5d71ae7fa4 100644 --- a/tests/ui/useless_vec.rs +++ b/tests/ui/useless_vec.rs @@ -1,15 +1,251 @@ -//@no-rustfix: no suggestions - #![warn(clippy::useless_vec)] -// Regression test for . -fn foo() { - // There should be no suggestion in this case. - let _some_variable = vec![ - //~^ useless_vec - 1, 2, // i'm here to stay - 3, 4, // but this one going away ;-; - ]; // that is life anyways +use std::rc::Rc; + +struct StructWithVec { + _x: Vec, } -fn main() {} +fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + +#[allow(clippy::ptr_arg)] +fn on_vec(_: &Vec) {} + +fn on_mut_vec(_: &mut Vec) {} + +struct Line { + length: usize, +} + +impl Line { + fn length(&self) -> usize { + self.length + } +} + +fn main() { + on_slice(&vec![]); + //~^ useless_vec + on_slice(&[]); + on_mut_slice(&mut vec![]); + //~^ useless_vec + + on_slice(&vec![1, 2]); + //~^ useless_vec + on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); + //~^ useless_vec + + #[rustfmt::skip] + #[allow(clippy::nonstandard_macro_braces)] // not an `expect` as it will only lint _before_ the fix + { + on_slice(&vec!(1, 2)); + //~^ useless_vec + on_mut_slice(&mut vec!(1, 2)); + //~^ useless_vec + }; + + on_slice(&vec![1; 2]); + //~^ useless_vec + on_slice(&[1; 2]); + on_mut_slice(&mut vec![1; 2]); + //~^ useless_vec + + on_vec(&vec![]); + on_vec(&vec![1, 2]); + on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); + + // Now with non-constant expressions + let line = Line { length: 2 }; + + on_slice(&vec![2; line.length]); + on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok, size of `vec` higher than `too_large_for_stack` + for a in vec![1; 201] { + println!("{a:?}"); + } + + // https://github.com/rust-lang/rust-clippy/issues/2262#issuecomment-783979246 + let _x: i32 = vec![1, 2, 3].iter().sum(); + //~^ useless_vec + + // Do lint, only used as slice + { + let mut x = vec![1, 2, 3]; + //~^ useless_vec + x.fill(123); + dbg!(x[0]); + dbg!(x.len()); + dbg!(x.iter().sum::()); + } + + let _x: &[i32] = &vec![1, 2, 3]; + //~^ useless_vec + + for _ in vec![1, 2, 3] {} + //~^ useless_vec + + // Don't lint + let x = vec![1, 2, 3]; + let _v: Vec = x; + + let x = vec![1, 2, 3]; + let _s = StructWithVec { _x: x }; + + // Explicit type annotation would make the change to [1, 2, 3] + // a compile error. + let _x: Vec = vec![1, 2, 3]; + + // Calling a Vec method through a mutable reference + let mut x = vec![1, 2, 3]; + let re = &mut x; + re.push(4); + + // Comparing arrays whose length is not equal is a compile error + let x = vec![1, 2, 3]; + let y = vec![1, 2, 3, 4]; + dbg!(x == y); + + // Non-copy types + let _x = vec![String::new(); 10]; + #[allow(clippy::rc_clone_in_vec_init)] + let _x = vec![Rc::new(1); 10]; + + // Too large + let _x = vec![1; 201]; +} + +fn issue11075() { + macro_rules! repro { + ($e:expr) => { + stringify!($e) + }; + } + #[allow(clippy::never_loop)] + for _string in vec![repro!(true), repro!(null)] { + //~^ useless_vec + unimplemented!(); + } + + macro_rules! in_macro { + ($e:expr, $vec:expr, $vec2:expr) => {{ + vec![1; 2].fill(3); + vec![1, 2].fill(3); + for _ in vec![1, 2] {} + for _ in vec![1; 2] {} + for _ in vec![$e, $e] {} + for _ in vec![$e; 2] {} + for _ in $vec {} + for _ in $vec2 {} + }}; + } + + in_macro!(1, vec![1, 2], vec![1; 2]); + //~^ useless_vec + //~| useless_vec + + macro_rules! from_macro { + () => { + vec![1, 2, 3] + }; + } + macro_rules! from_macro_repeat { + () => { + vec![1; 3] + }; + } + + for _ in from_macro!() {} + for _ in from_macro_repeat!() {} +} + +#[clippy::msrv = "1.53"] +fn above() { + for a in vec![1, 2, 3] { + //~^ useless_vec + let _: usize = a; + } + + for a in vec![String::new(), String::new()] { + //~^ useless_vec + let _: String = a; + } +} + +#[clippy::msrv = "1.52"] +fn below() { + for a in vec![1, 2, 3] { + let _: usize = a; + } + + for a in vec![String::new(), String::new()] { + let _: String = a; + } +} + +fn func_needing_vec(_bar: usize, _baz: Vec) {} +fn func_not_needing_vec(_bar: usize, _baz: usize) {} + +fn issue11861() { + macro_rules! this_macro_needs_vec { + ($x:expr) => {{ + func_needing_vec($x.iter().sum(), $x); + for _ in $x {} + }}; + } + macro_rules! this_macro_doesnt_need_vec { + ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }}; + } + + // Do not lint the next line + this_macro_needs_vec!(vec![1]); + this_macro_doesnt_need_vec!(vec![1]); + //~^ useless_vec + + macro_rules! m { + ($x:expr) => { + fn f2() { + let _x: Vec = $x; + } + fn f() { + let _x = $x; + $x.starts_with(&[]); + } + }; + } + + // should not lint + m!(vec![1]); +} + +fn issue_11958() { + fn f(_s: &[String]) {} + + // should not lint, `String` is not `Copy` + f(&vec!["test".to_owned(); 2]); +} + +fn issue_12101() { + for a in &(vec![1, 2]) {} + //~^ useless_vec +} + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &vec![]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/useless_vec.stderr b/tests/ui/useless_vec.stderr index e47364fb06d3..65120d8b338f 100644 --- a/tests/ui/useless_vec.stderr +++ b/tests/ui/useless_vec.stderr @@ -1,21 +1,125 @@ error: useless use of `vec!` - --> tests/ui/useless_vec.rs:8:26 + --> tests/ui/useless_vec.rs:29:14 | -LL | let _some_variable = vec![ - | __________________________^ -LL | | -LL | | 1, 2, // i'm here to stay -LL | | 3, 4, // but this one going away ;-; -LL | | ]; // that is life anyways - | |_____^ +LL | on_slice(&vec![]); + | ^^^^^^^ help: you can use a slice directly: `&[]` | = note: `-D clippy::useless-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` -help: you can use an array directly - | -LL ~ let _some_variable = [1, 2, // i'm here to stay -LL ~ 3, 4]; // that is life anyways - | -error: aborting due to 1 previous error +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:32:18 + | +LL | on_mut_slice(&mut vec![]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:35:14 + | +LL | on_slice(&vec![1, 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:38:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:44:18 + | +LL | on_slice(&vec!(1, 2)); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:46:22 + | +LL | on_mut_slice(&mut vec!(1, 2)); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:50:14 + | +LL | on_slice(&vec![1; 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:53:18 + | +LL | on_mut_slice(&mut vec![1; 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:80:19 + | +LL | let _x: i32 = vec![1, 2, 3].iter().sum(); + | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:85:21 + | +LL | let mut x = vec![1, 2, 3]; + | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:93:22 + | +LL | let _x: &[i32] = &vec![1, 2, 3]; + | ^^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:96:14 + | +LL | for _ in vec![1, 2, 3] {} + | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:136:20 + | +LL | for _string in vec![repro!(true), repro!(null)] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:154:18 + | +LL | in_macro!(1, vec![1, 2], vec![1; 2]); + | ^^^^^^^^^^ help: you can use an array directly: `[1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:154:30 + | +LL | in_macro!(1, vec![1, 2], vec![1; 2]); + | ^^^^^^^^^^ help: you can use an array directly: `[1; 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:175:14 + | +LL | for a in vec![1, 2, 3] { + | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:180:14 + | +LL | for a in vec![String::new(), String::new()] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:213:33 + | +LL | this_macro_doesnt_need_vec!(vec![1]); + | ^^^^^^^ help: you can use an array directly: `[1]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:240:14 + | +LL | for a in &(vec![1, 2]) {} + | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:248:13 + | +LL | let v = &vec![]; + | ^^^^^^^ help: you can use a slice directly: `&[]` + +error: aborting due to 20 previous errors diff --git a/tests/ui/useless_vec_unfixable.rs b/tests/ui/useless_vec_unfixable.rs new file mode 100644 index 000000000000..7f45f4df5ee6 --- /dev/null +++ b/tests/ui/useless_vec_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix: no suggestions +#![warn(clippy::useless_vec)] + +// Regression test for . +fn foo() { + // There should be no suggestion in this case. + let _some_variable = vec![ + //~^ useless_vec + 1, 2, // i'm here to stay + 3, 4, // but this one going away ;-; + ]; // that is life anyways +} + +fn main() {} diff --git a/tests/ui/useless_vec_unfixable.stderr b/tests/ui/useless_vec_unfixable.stderr new file mode 100644 index 000000000000..980194ac7191 --- /dev/null +++ b/tests/ui/useless_vec_unfixable.stderr @@ -0,0 +1,21 @@ +error: useless use of `vec!` + --> tests/ui/useless_vec_unfixable.rs:7:26 + | +LL | let _some_variable = vec![ + | __________________________^ +LL | | +LL | | 1, 2, // i'm here to stay +LL | | 3, 4, // but this one going away ;-; +LL | | ]; // that is life anyways + | |_____^ + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +help: you can use an array directly + | +LL ~ let _some_variable = [1, 2, // i'm here to stay +LL ~ 3, 4]; // that is life anyways + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs deleted file mode 100644 index fbf7131323c3..000000000000 --- a/tests/ui/vec.rs +++ /dev/null @@ -1,253 +0,0 @@ -#![warn(clippy::useless_vec)] -#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)] - -use std::rc::Rc; - -struct StructWithVec { - _x: Vec, -} - -fn on_slice(_: &[u8]) {} - -fn on_mut_slice(_: &mut [u8]) {} - -#[allow(clippy::ptr_arg)] -fn on_vec(_: &Vec) {} - -fn on_mut_vec(_: &mut Vec) {} - -struct Line { - length: usize, -} - -impl Line { - fn length(&self) -> usize { - self.length - } -} - -fn main() { - on_slice(&vec![]); - //~^ useless_vec - on_slice(&[]); - on_mut_slice(&mut vec![]); - //~^ useless_vec - - on_slice(&vec![1, 2]); - //~^ useless_vec - on_slice(&[1, 2]); - on_mut_slice(&mut vec![1, 2]); - //~^ useless_vec - - on_slice(&vec![1, 2]); - //~^ useless_vec - on_slice(&[1, 2]); - on_mut_slice(&mut vec![1, 2]); - //~^ useless_vec - #[rustfmt::skip] - on_slice(&vec!(1, 2)); - //~^ useless_vec - on_slice(&[1, 2]); - on_mut_slice(&mut vec![1, 2]); - //~^ useless_vec - - on_slice(&vec![1; 2]); - //~^ useless_vec - on_slice(&[1; 2]); - on_mut_slice(&mut vec![1; 2]); - //~^ useless_vec - - on_vec(&vec![]); - on_vec(&vec![1, 2]); - on_vec(&vec![1; 2]); - on_mut_vec(&mut vec![]); - on_mut_vec(&mut vec![1, 2]); - on_mut_vec(&mut vec![1; 2]); - - // Now with non-constant expressions - let line = Line { length: 2 }; - - on_slice(&vec![2; line.length]); - on_slice(&vec![2; line.length()]); - on_mut_slice(&mut vec![2; line.length]); - on_mut_slice(&mut vec![2; line.length()]); - - on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` - on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` - - // Ok - for a in vec![1; 201] { - println!("{:?}", a); - } - - // https://github.com/rust-lang/rust-clippy/issues/2262#issuecomment-783979246 - let _x: i32 = vec![1, 2, 3].iter().sum(); - //~^ useless_vec - - // Do lint - let mut x = vec![1, 2, 3]; - //~^ useless_vec - x.fill(123); - dbg!(x[0]); - dbg!(x.len()); - dbg!(x.iter().sum::()); - - let _x: &[i32] = &vec![1, 2, 3]; - //~^ useless_vec - - for _ in vec![1, 2, 3] {} - //~^ useless_vec - - // Don't lint - let x = vec![1, 2, 3]; - let _v: Vec = x; - - let x = vec![1, 2, 3]; - let _s = StructWithVec { _x: x }; - - // Explicit type annotation would make the change to [1, 2, 3] - // a compile error. - let _x: Vec = vec![1, 2, 3]; - - // Calling a Vec method through a mutable reference - let mut x = vec![1, 2, 3]; - let re = &mut x; - re.push(4); - - // Comparing arrays whose length is not equal is a compile error - let x = vec![1, 2, 3]; - let y = vec![1, 2, 3, 4]; - dbg!(x == y); - - // Non-copy types - let _x = vec![String::new(); 10]; - #[allow(clippy::rc_clone_in_vec_init)] - let _x = vec![Rc::new(1); 10]; - - // Too large - let _x = vec![1; 201]; -} - -fn issue11075() { - macro_rules! repro { - ($e:expr) => { - stringify!($e) - }; - } - #[allow(clippy::never_loop)] - for _string in vec![repro!(true), repro!(null)] { - //~^ useless_vec - unimplemented!(); - } - - macro_rules! in_macro { - ($e:expr, $vec:expr, $vec2:expr) => {{ - vec![1; 2].fill(3); - vec![1, 2].fill(3); - for _ in vec![1, 2] {} - for _ in vec![1; 2] {} - for _ in vec![$e, $e] {} - for _ in vec![$e; 2] {} - for _ in $vec {} - for _ in $vec2 {} - }}; - } - - in_macro!(1, vec![1, 2], vec![1; 2]); - //~^ useless_vec - //~| useless_vec - - macro_rules! from_macro { - () => { - vec![1, 2, 3] - }; - } - macro_rules! from_macro_repeat { - () => { - vec![1; 3] - }; - } - - for _ in from_macro!() {} - for _ in from_macro_repeat!() {} -} - -#[clippy::msrv = "1.53"] -fn above() { - for a in vec![1, 2, 3] { - //~^ useless_vec - let _: usize = a; - } - - for a in vec![String::new(), String::new()] { - //~^ useless_vec - let _: String = a; - } -} - -#[clippy::msrv = "1.52"] -fn below() { - for a in vec![1, 2, 3] { - let _: usize = a; - } - - for a in vec![String::new(), String::new()] { - let _: String = a; - } -} - -fn func_needing_vec(_bar: usize, _baz: Vec) {} -fn func_not_needing_vec(_bar: usize, _baz: usize) {} - -fn issue11861() { - macro_rules! this_macro_needs_vec { - ($x:expr) => {{ - func_needing_vec($x.iter().sum(), $x); - for _ in $x {} - }}; - } - macro_rules! this_macro_doesnt_need_vec { - ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }}; - } - - // Do not lint the next line - this_macro_needs_vec!(vec![1]); - this_macro_doesnt_need_vec!(vec![1]); - //~^ useless_vec - - macro_rules! m { - ($x:expr) => { - fn f2() { - let _x: Vec = $x; - } - fn f() { - let _x = $x; - $x.starts_with(&[]); - } - }; - } - - // should not lint - m!(vec![1]); -} - -fn issue_11958() { - fn f(_s: &[String]) {} - - // should not lint, `String` is not `Copy` - f(&vec!["test".to_owned(); 2]); -} - -fn issue_12101() { - for a in &(vec![1, 2]) {} - //~^ useless_vec -} - -fn issue_14531() { - // The lint used to suggest using an array rather than a reference to a slice. - - fn requires_ref_slice(v: &[()]) {} - let v = &vec![]; - //~^ useless_vec - requires_ref_slice(v); -} diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr deleted file mode 100644 index d16c8a8944a2..000000000000 --- a/tests/ui/vec.stderr +++ /dev/null @@ -1,137 +0,0 @@ -error: useless use of `vec!` - --> tests/ui/vec.rs:30:14 - | -LL | on_slice(&vec![]); - | ^^^^^^^ help: you can use a slice directly: `&[]` - | - = note: `-D clippy::useless-vec` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:33:18 - | -LL | on_mut_slice(&mut vec![]); - | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []` - -error: useless use of `vec!` - --> tests/ui/vec.rs:36:14 - | -LL | on_slice(&vec![1, 2]); - | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:39:18 - | -LL | on_mut_slice(&mut vec![1, 2]); - | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:42:14 - | -LL | on_slice(&vec![1, 2]); - | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:45:18 - | -LL | on_mut_slice(&mut vec![1, 2]); - | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:48:14 - | -LL | on_slice(&vec!(1, 2)); - | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:51:18 - | -LL | on_mut_slice(&mut vec![1, 2]); - | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:54:14 - | -LL | on_slice(&vec![1; 2]); - | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:57:18 - | -LL | on_mut_slice(&mut vec![1; 2]); - | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:84:19 - | -LL | let _x: i32 = vec![1, 2, 3].iter().sum(); - | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:88:17 - | -LL | let mut x = vec![1, 2, 3]; - | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:95:22 - | -LL | let _x: &[i32] = &vec![1, 2, 3]; - | ^^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:98:14 - | -LL | for _ in vec![1, 2, 3] {} - | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:138:20 - | -LL | for _string in vec![repro!(true), repro!(null)] { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:156:18 - | -LL | in_macro!(1, vec![1, 2], vec![1; 2]); - | ^^^^^^^^^^ help: you can use an array directly: `[1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:156:30 - | -LL | in_macro!(1, vec![1, 2], vec![1; 2]); - | ^^^^^^^^^^ help: you can use an array directly: `[1; 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:177:14 - | -LL | for a in vec![1, 2, 3] { - | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:182:14 - | -LL | for a in vec![String::new(), String::new()] { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:215:33 - | -LL | this_macro_doesnt_need_vec!(vec![1]); - | ^^^^^^^ help: you can use an array directly: `[1]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:242:14 - | -LL | for a in &(vec![1, 2]) {} - | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` - -error: useless use of `vec!` - --> tests/ui/vec.rs:250:13 - | -LL | let v = &vec![]; - | ^^^^^^^ help: you can use a slice directly: `&[]` - -error: aborting due to 22 previous errors - diff --git a/triagebot.toml b/triagebot.toml index db951b95ef50..3bf62b6b3bba 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -63,7 +63,8 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "blyxyas", + "Alexendoo", + "y21", ] [assign.owners] diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index e443baff0808..91a5c1263195 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -57,118 +57,114 @@ Otherwise, have a great day =^.^= {# #} {% for lint in lints %} diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 18bb95cf67b6..242e2227ed94 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -113,29 +113,26 @@ label { background: var(--bg); border: 1px solid var(--theme-popup-border); box-shadow: 0 1px 1px rgba(0,0,0,.05); + display: block; } -div.panel-body { - padding: 15px; +#menu-filters { + padding: 15px 0; + display: flex; + flex-direction: column; } -div.panel-body::before, div.panel-body::after { - display: table; - content: " "; -} -div.panel-body::after { - clear: both; -} -div.panel-body button { + +#menu-filters button { background: var(--searchbar-bg); border-color: var(--theme-popup-border); color: var(--searchbar-fg); } -div.panel-body button:hover { +#menu-filters button:hover { box-shadow: 0 0 3px var(--searchbar-shadow-color); } -div.panel-body button.open { +#menu-filters button.open { filter: brightness(90%); } @@ -242,33 +239,10 @@ article:hover .panel-title-name .anchor { display: inline;} min-height: 1px; padding-right: 15px; padding-left: 15px; -} - -.input-group { - position: relative; display: flex; } -.input-group > :last-child { - border-left: 0; -} -.input-group > :first-child, .btn-group > :first-child { - border-right: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group > :last-child, .btn-group > :last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6); -} -.input-group-addon { + +#filter-label { padding: 6px 12px; font-size: 14px; font-weight: 400; @@ -280,6 +254,29 @@ article:hover .panel-title-name .anchor { display: inline;} display: flex; align-items: center; justify-content: center; + border-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.search-control > :last-child { + border-left: 0; +} +.btn-group > :first-child { + border-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.search-control > :last-child, .btn-group > :last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.search-control .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6); } .glyphicon.glyphicon-collapse-up::before, .glyphicon.glyphicon-collapse-down::before { @@ -343,6 +340,7 @@ article:hover .panel-title-name .anchor { display: inline;} @media (min-width: 992px) { .search-control { margin-top: 0; + align-self: flex-start; } .container { width: 970px; @@ -357,6 +355,10 @@ article:hover .panel-title-name .anchor { display: inline;} margin-top: 0; padding: 0px 15px; width: 16.66666667%; + align-self: flex-start; + } + #menu-filters { + flex-direction: row; } } @@ -376,7 +378,7 @@ article:hover .panel-title-name .anchor { display: inline;} @media (max-width: 412px) { #upper-filters, - .panel-body .search-control { + #menu-filters .search-control { padding-right: 8px; padding-left: 8px; } @@ -617,6 +619,7 @@ L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.649 color: var(--fg); background: var(--theme-hover); border: 1px solid var(--theme-popup-border); + padding: 8px; } .page-header { border: 0; From bbadb72919e510c3a98cbe303cac2f58e0c5d065 Mon Sep 17 00:00:00 2001 From: Sergio Giro Date: Sat, 29 Nov 2025 18:40:32 +0000 Subject: [PATCH 15/56] while_let_on_iterator: consider all deref adjustments for by_ref --- .../src/loops/while_let_on_iterator.rs | 30 ++++---- tests/ui/while_let_on_iterator.fixed | 73 +++++++++++++++++++ tests/ui/while_let_on_iterator.rs | 73 +++++++++++++++++++ tests/ui/while_let_on_iterator.stderr | 22 +++++- 4 files changed, 182 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index c063e9263ba0..6c95c7b54c50 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,7 +12,6 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -52,9 +51,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { || !iter_expr_struct.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr_struct, expr) { - make_iterator_snippet(cx, iter_expr, iterator) + make_iterator_snippet(cx, iter_expr, &iterator) } else { - iterator.to_string() + iterator.into_owned() }; span_lint_and_sugg( @@ -358,17 +357,20 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & } /// Constructs the transformed iterator expression for the suggestion. -/// Returns `iterator.by_ref()` unless the iterator type is a reference to an unsized type, -/// in which case it returns `&mut *iterator`. -fn make_iterator_snippet<'tcx>(cx: &LateContext<'tcx>, iter_expr: &Expr<'tcx>, iterator: impl Into) -> String { - let iterator = iterator.into(); - let ty = cx.typeck_results().expr_ty(iter_expr); - - if let ty::Ref(_, inner_ty, _) = ty.kind() - && !inner_ty.is_sized(cx.tcx, cx.typing_env()) +/// Returns `iterator.by_ref()` unless the last deref adjustment targets an unsized type, +/// in which case it applies all derefs (e.g., `&mut **iterator` or `&mut ***iterator`). +fn make_iterator_snippet<'tcx>(cx: &LateContext<'tcx>, iter_expr: &Expr<'tcx>, iterator: &str) -> String { + if let Some((n, adjust)) = cx + .typeck_results() + .expr_adjustments(iter_expr) + .iter() + .take_while(|x| matches!(x.kind, Adjust::Deref(_))) + .enumerate() + .last() + && !adjust.target.is_sized(cx.tcx, cx.typing_env()) { - return format!("&mut *{iterator}"); + format!("&mut {:*(T); + impl core::ops::Deref for S { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for S { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn f(mut x: S>>) { + for _ in &mut ***x {} + //~^ while_let_on_iterator + } +} + +fn issue16089_nested_derefs_last_not_sized() { + struct WithSize(T); + impl core::ops::Deref for WithSize { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for WithSize { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + // The suggestion must use `&mut **x`. Using `x.by_ref()` doesn't work in this + // case, since the last type adjustment for `x` in the expression `x.next()` is + // to dereference a `?Sized` trait. + fn f(mut x: WithSize<&mut dyn Iterator>) { + for _ in &mut **x {} + //~^ while_let_on_iterator + } +} + +fn issue16089_nested_derefs_last_sized() { + struct NoSize(T); + impl core::ops::Deref for NoSize { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for NoSize { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct SizedIter {} + + impl Iterator for SizedIter { + type Item = u32; + fn next(&mut self) -> Option { + Some(0) + } + } + + // We want the suggestion to be `x.by_ref()`. It works in this case since the last type + // adjustment for `x` in the expression `x.next()` is to dereference a Sized type. + fn f(mut x: NoSize>) { + for _ in x.by_ref() {} + //~^ while_let_on_iterator + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index cc65fda6d18f..e1d9e9081e45 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -523,6 +523,79 @@ fn issue16089_sized_trait_not_reborrowed() { } } +fn issue16089_nested_derefs() { + struct S(T); + impl core::ops::Deref for S { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for S { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn f(mut x: S>>) { + while let Some(_) = x.next() {} + //~^ while_let_on_iterator + } +} + +fn issue16089_nested_derefs_last_not_sized() { + struct WithSize(T); + impl core::ops::Deref for WithSize { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for WithSize { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + // The suggestion must use `&mut **x`. Using `x.by_ref()` doesn't work in this + // case, since the last type adjustment for `x` in the expression `x.next()` is + // to dereference a `?Sized` trait. + fn f(mut x: WithSize<&mut dyn Iterator>) { + while let Some(_) = x.next() {} + //~^ while_let_on_iterator + } +} + +fn issue16089_nested_derefs_last_sized() { + struct NoSize(T); + impl core::ops::Deref for NoSize { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for NoSize { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct SizedIter {} + + impl Iterator for SizedIter { + type Item = u32; + fn next(&mut self) -> Option { + Some(0) + } + } + + // We want the suggestion to be `x.by_ref()`. It works in this case since the last type + // adjustment for `x` in the expression `x.next()` is to dereference a Sized type. + fn f(mut x: NoSize>) { + while let Some(_) = x.next() {} + //~^ while_let_on_iterator + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 21ebc22f699d..cd43d3c17800 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -176,10 +176,28 @@ LL | while let Some(r) = self.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in self.by_ref()` error: this loop could be written as a `for` loop - --> tests/ui/while_let_on_iterator.rs:528:5 + --> tests/ui/while_let_on_iterator.rs:541:9 + | +LL | while let Some(_) = x.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in &mut ***x` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:563:9 + | +LL | while let Some(_) = x.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in &mut **x` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:594:9 + | +LL | while let Some(_) = x.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in x.by_ref()` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:601:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 30 previous errors +error: aborting due to 33 previous errors From 9cb6306a48f02d96ee839398e85b17e181fa3844 Mon Sep 17 00:00:00 2001 From: Artur Sulej Date: Sun, 23 Nov 2025 22:51:03 +0100 Subject: [PATCH 16/56] New lint: decimal_bitwise_operands --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/operators/decimal_bitwise_operands.rs | 76 +++++++ clippy_lints/src/operators/mod.rs | 28 +++ tests/ui/decimal_bitwise_operands.rs | 133 ++++++++++++ tests/ui/decimal_bitwise_operands.stderr | 204 ++++++++++++++++++ 6 files changed, 443 insertions(+) create mode 100644 clippy_lints/src/operators/decimal_bitwise_operands.rs create mode 100644 tests/ui/decimal_bitwise_operands.rs create mode 100644 tests/ui/decimal_bitwise_operands.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b81b5b74d6..dc93753a2130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6280,6 +6280,7 @@ Released 2018-09-13 [`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call +[`decimal_bitwise_operands`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_bitwise_operands [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const [`default_constructed_unit_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_constructed_unit_structs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4a350dca2993..de7d01ce0d1b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -578,6 +578,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::ASSIGN_OP_PATTERN_INFO, crate::operators::BAD_BIT_MASK_INFO, crate::operators::CMP_OWNED_INFO, + crate::operators::DECIMAL_BITWISE_OPERANDS_INFO, crate::operators::DOUBLE_COMPARISONS_INFO, crate::operators::DURATION_SUBSEC_INFO, crate::operators::EQ_OP_INFO, diff --git a/clippy_lints/src/operators/decimal_bitwise_operands.rs b/clippy_lints/src/operators/decimal_bitwise_operands.rs new file mode 100644 index 000000000000..8511f2151342 --- /dev/null +++ b/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -0,0 +1,76 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::numeric_literal; +use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::source::SpanRangeExt; +use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::DECIMAL_BITWISE_OPERANDS; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, left: &'tcx Expr<'_>, right: &'tcx Expr<'_>) { + if !matches!(op, BinOpKind::BitAnd | BinOpKind::BitOr | BinOpKind::BitXor) { + return; + } + + for expr in [left, right] { + check_expr(cx, expr); + } +} + +fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + match &expr.kind { + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr { + check_expr(cx, block_expr); + } + }, + ExprKind::Cast(cast_expr, _) => { + check_expr(cx, cast_expr); + }, + ExprKind::Unary(_, unary_expr) => { + check_expr(cx, unary_expr); + }, + ExprKind::AddrOf(_, _, addr_of_expr) => { + check_expr(cx, addr_of_expr); + }, + ExprKind::Lit(lit) => { + if let LitKind::Int(Pu128(val), _) = lit.node + && !is_single_digit(val) + && !is_power_of_twoish(val) + && let Some(src) = lit.span.get_source_text(cx) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + && num_lit.is_decimal() + { + emit_lint(cx, lit.span, num_lit.suffix, val); + } + }, + _ => (), + } +} + +fn is_power_of_twoish(val: u128) -> bool { + val.is_power_of_two() || val.wrapping_add(1).is_power_of_two() +} + +fn is_single_digit(val: u128) -> bool { + val <= 9 +} + +fn emit_lint(cx: &LateContext<'_>, span: Span, suffix: Option<&str>, val: u128) { + span_lint_and_help( + cx, + DECIMAL_BITWISE_OPERANDS, + span, + "using decimal literal for bitwise operation", + None, + format!( + "use binary ({}), hex ({}), or octal ({}) notation for better readability", + numeric_literal::format(&format!("{val:#b}"), suffix, false), + numeric_literal::format(&format!("{val:#x}"), suffix, false), + numeric_literal::format(&format!("{val:#o}"), suffix, false), + ), + ); +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 8db2cc1d3f57..53b8e9e5d5ae 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -3,6 +3,7 @@ mod assign_op_pattern; mod bit_mask; mod cmp_owned; mod const_comparisons; +mod decimal_bitwise_operands; mod double_comparison; mod duration_subsec; mod eq_op; @@ -935,6 +936,28 @@ declare_clippy_lint! { "use of disallowed default division and remainder operations" } +declare_clippy_lint! { + /// ### What it does + /// Checks for decimal literals used as bit masks in bitwise operations. + /// + /// ### Why is this bad? + /// Using decimal literals for bit masks can make the code less readable and obscure the intended bit pattern. + /// Binary, hexadecimal, or octal literals make the bit pattern more explicit and easier to understand at a glance. + /// + /// ### Example + /// ```rust,no_run + /// let a = 14 & 6; // Bit pattern is not immediately clear + /// ``` + /// Use instead: + /// ```rust,no_run + /// let a = 0b1110 & 0b0110; + /// ``` + #[clippy::version = "1.93.0"] + pub DECIMAL_BITWISE_OPERANDS, + pedantic, + "use binary, hex, or octal literals for bitwise operations" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, @@ -984,6 +1007,7 @@ impl_lint_pass!(Operators => [ MANUAL_IS_MULTIPLE_OF, MANUAL_DIV_CEIL, INVALID_UPCAST_COMPARISONS, + DECIMAL_BITWISE_OPERANDS ]); impl<'tcx> LateLintPass<'tcx> for Operators { @@ -1003,6 +1027,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv); + decimal_bitwise_operands::check(cx, op.node, lhs, rhs); } self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); bit_mask::check(cx, e, op.node, lhs, rhs); @@ -1028,6 +1053,9 @@ impl<'tcx> LateLintPass<'tcx> for Operators { }, ExprKind::AssignOp(op, lhs, rhs) => { let bin_op = op.node.into(); + if !e.span.from_expansion() { + decimal_bitwise_operands::check(cx, bin_op, lhs, rhs); + } self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs); misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs); modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); diff --git a/tests/ui/decimal_bitwise_operands.rs b/tests/ui/decimal_bitwise_operands.rs new file mode 100644 index 000000000000..f1c053bb4358 --- /dev/null +++ b/tests/ui/decimal_bitwise_operands.rs @@ -0,0 +1,133 @@ +#![allow( + clippy::erasing_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::unnecessary_cast, + clippy::op_ref +)] +#![warn(clippy::decimal_bitwise_operands)] + +macro_rules! bitwise_op { + ($x:expr, $y:expr) => { + $x & $y; + }; +} + +pub const SOME_CONST: i32 = 12345; + +fn main() { + let mut x = 0; + // BAD: Bitwise operation, decimal literal, one literal + x & 9_8765_4321; //~ decimal_bitwise_operands + x & 100_i32; //~ decimal_bitwise_operands + x | (/* comment */99); //~ decimal_bitwise_operands + x ^ (99); //~ decimal_bitwise_operands + x &= 99; //~ decimal_bitwise_operands + x |= { 99 }; //~ decimal_bitwise_operands + x |= { { 99 } }; //~ decimal_bitwise_operands + x |= { + 0b1000; + 99 //~ decimal_bitwise_operands + }; + x ^= (99); //~ decimal_bitwise_operands + + // BAD: Bitwise operation, decimal literal, two literals + 0b1010 & 99; //~ decimal_bitwise_operands + 0b1010 | (99); //~ decimal_bitwise_operands + 0b1010 ^ (/* comment */99); //~ decimal_bitwise_operands + 99 & 0b1010; //~ decimal_bitwise_operands + (99) | 0b1010; //~ decimal_bitwise_operands + (/* comment */99) ^ 0b1010; //~ decimal_bitwise_operands + 0xD | { 99 }; //~ decimal_bitwise_operands + 88 & 99; + //~^ decimal_bitwise_operands + //~| decimal_bitwise_operands + 37 & 38 & 39; + //~^ decimal_bitwise_operands + //~| decimal_bitwise_operands + //~| decimal_bitwise_operands + + // GOOD: Bitwise operation, binary/hex/octal literal, one literal + x & 0b1010; + x | 0b1010; + x ^ 0b1010; + x &= 0b1010; + x |= 0b1010; + x ^= 0b1010; + x & 0xD; + x & 0o77; + x | 0o123; + x ^ 0o377; + x &= 0o777; + x |= 0o7; + x ^= 0o70; + + // GOOD: Bitwise operation, binary/hex/octal literal, two literals + 0b1010 & 0b1101; + 0xD ^ 0xF; + 0o377 ^ 0o77; + 0b1101 ^ 0xFF; + + // GOOD: Numeric operation, any literal + x += 99; + x -= 0b1010; + x *= 0xD; + 99 + 99; + 0b1010 - 0b1101; + 0xD * 0xD; + + // BAD: Unary, cast and reference, decimal literal + x & !100; //~ decimal_bitwise_operands + x & -100; //~ decimal_bitwise_operands + x & (100 as i32); //~ decimal_bitwise_operands + x & &100; //~ decimal_bitwise_operands + + // GOOD: Unary, cast and reference, non-decimal literal + x & !0b1101; + x & -0xD; + x & (0o333 as i32); + x & &0b1010; + + // GOOD: Bitwise operation, variables only + let y = 0; + x & y; + x &= y; + x + y; + x += y; + + // GOOD: Macro expansion (should be ignored) + bitwise_op!(x, 123); + bitwise_op!(0b1010, 123); + + // GOOD: Using const (should be ignored) + x & SOME_CONST; + x |= SOME_CONST; + + // GOOD: Parenthesized binary/hex literal (should not trigger lint) + x & (0b1111); + x |= (0b1010); + x ^ (/* comment */0b1100); + (0xFF) & x; + + // GOOD: Power of two and power of two minus one + x & 16; // 2^4 + x | (31); // 2^5 - 1 + x ^ 0x40; // 2^6 (hex) + x ^= 7; // 2^3 - 1 + + // GOOD: Bitwise operation, single digit decimal literal + 5 & 9; + x ^ 6; + x ^= 7; + + // GOOD: More complex expressions + (x + 1) & 0xFF; + (x * 2) | (y & 0xF); + (x ^ y) & 0b11110000; + x | (1 << 9); + + // GOOD: Special cases + x & 0; // All bits off + x | !0; // All bits on + x ^ 1; // Toggle LSB +} diff --git a/tests/ui/decimal_bitwise_operands.stderr b/tests/ui/decimal_bitwise_operands.stderr new file mode 100644 index 000000000000..1b2b7bb71b69 --- /dev/null +++ b/tests/ui/decimal_bitwise_operands.stderr @@ -0,0 +1,204 @@ +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:21:9 + | +LL | x & 9_8765_4321; + | ^^^^^^^^^^^ + | + = help: use binary (0b11_1010_1101_1110_0110_1000_1011_0001), hex (0x3ade_68b1), or octal (0o7_267_464_261) notation for better readability + = note: `-D clippy::decimal-bitwise-operands` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::decimal_bitwise_operands)]` + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:22:9 + | +LL | x & 100_i32; + | ^^^^^^^ + | + = help: use binary (0b110_0100_i32), hex (0x0064_i32), or octal (0o144_i32) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:23:23 + | +LL | x | (/* comment */99); + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:24:10 + | +LL | x ^ (99); + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:25:10 + | +LL | x &= 99; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:26:12 + | +LL | x |= { 99 }; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:27:14 + | +LL | x |= { { 99 } }; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:30:9 + | +LL | 99 + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:32:11 + | +LL | x ^= (99); + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:35:14 + | +LL | 0b1010 & 99; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:36:15 + | +LL | 0b1010 | (99); + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:37:28 + | +LL | 0b1010 ^ (/* comment */99); + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:38:5 + | +LL | 99 & 0b1010; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:39:6 + | +LL | (99) | 0b1010; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:40:19 + | +LL | (/* comment */99) ^ 0b1010; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:41:13 + | +LL | 0xD | { 99 }; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:42:5 + | +LL | 88 & 99; + | ^^ + | + = help: use binary (0b101_1000), hex (0x0058), or octal (0o130) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:42:10 + | +LL | 88 & 99; + | ^^ + | + = help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:45:15 + | +LL | 37 & 38 & 39; + | ^^ + | + = help: use binary (0b10_0111), hex (0x0027), or octal (0o47) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:45:5 + | +LL | 37 & 38 & 39; + | ^^ + | + = help: use binary (0b10_0101), hex (0x0025), or octal (0o45) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:45:10 + | +LL | 37 & 38 & 39; + | ^^ + | + = help: use binary (0b10_0110), hex (0x0026), or octal (0o46) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:80:10 + | +LL | x & !100; + | ^^^ + | + = help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:81:10 + | +LL | x & -100; + | ^^^ + | + = help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:82:10 + | +LL | x & (100 as i32); + | ^^^ + | + = help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability + +error: using decimal literal for bitwise operation + --> tests/ui/decimal_bitwise_operands.rs:83:10 + | +LL | x & &100; + | ^^^ + | + = help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability + +error: aborting due to 25 previous errors + From a6162c3dd9e8b620c277f6b90df1ed3f669864f8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Sep 2025 15:53:13 +0200 Subject: [PATCH 17/56] add `ptr_offset_by_literal` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 37 +++++ .../src/methods/ptr_offset_by_literal.rs | 138 +++++++++++++++++ tests/ui/borrow_as_ptr.fixed | 2 +- tests/ui/borrow_as_ptr.rs | 2 +- tests/ui/crashes/ice-4579.rs | 2 +- tests/ui/ptr_offset_by_literal.fixed | 50 +++++++ tests/ui/ptr_offset_by_literal.rs | 50 +++++++ tests/ui/ptr_offset_by_literal.stderr | 141 ++++++++++++++++++ tests/ui/zero_offset.rs | 2 +- 11 files changed, 422 insertions(+), 4 deletions(-) create mode 100644 clippy_lints/src/methods/ptr_offset_by_literal.rs create mode 100644 tests/ui/ptr_offset_by_literal.fixed create mode 100644 tests/ui/ptr_offset_by_literal.rs create mode 100644 tests/ui/ptr_offset_by_literal.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb2755be0ee..434cfff20510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6764,6 +6764,7 @@ Released 2018-09-13 [`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq +[`ptr_offset_by_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_by_literal [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names [`pub_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a754eea31165..1414e6076364 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -443,6 +443,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::PATH_ENDS_WITH_EXT_INFO, + crate::methods::PTR_OFFSET_BY_LITERAL_INFO, crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c22b0a548e3d..e6b9589233c9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -94,6 +94,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod path_ends_with_ext; +mod ptr_offset_by_literal; mod ptr_offset_with_cast; mod range_zip_with_len; mod read_line_without_trim; @@ -1728,6 +1729,40 @@ declare_clippy_lint! { "Check for offset calculations on raw pointers to zero-sized types" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `offset` pointer method with an integer + /// literal. + /// + /// ### Why is this bad? + /// The `add` and `sub` methods more accurately express the intent. + /// + /// ### Example + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// + /// unsafe { + /// ptr.offset(-8); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// + /// unsafe { + /// ptr.sub(8); + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub PTR_OFFSET_BY_LITERAL, + pedantic, + "unneeded pointer offset" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of the `offset` pointer method with a `usize` casted to an @@ -4803,6 +4838,7 @@ impl_lint_pass!(Methods => [ UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, ZST_OFFSET, + PTR_OFFSET_BY_LITERAL, PTR_OFFSET_WITH_CAST, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, @@ -5426,6 +5462,7 @@ impl Methods { zst_offset::check(cx, expr, recv); ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv); + ptr_offset_by_literal::check(cx, expr, self.msrv); }, (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); diff --git a/clippy_lints/src/methods/ptr_offset_by_literal.rs b/clippy_lints/src/methods/ptr_offset_by_literal.rs new file mode 100644 index 000000000000..b5d2add65cf1 --- /dev/null +++ b/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -0,0 +1,138 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Lit, UnOp}; +use rustc_lint::LateContext; +use std::cmp::Ordering; +use std::fmt; + +use super::PTR_OFFSET_BY_LITERAL; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Msrv) { + // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions + // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable. + if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) { + return; + } + + let ExprKind::MethodCall(method_name, recv, [arg_expr], _) = expr.kind else { + return; + }; + + let method = match method_name.ident.name { + sym::offset => Method::Offset, + sym::wrapping_offset => Method::WrappingOffset, + _ => return, + }; + + if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() { + return; + } + + // Check if the argument to the method call is a (negated) literal. + let Some((literal, literal_text)) = expr_as_literal(cx, arg_expr) else { + return; + }; + + match method.suggestion(literal) { + None => { + let msg = format!("use of `{method}` with zero"); + span_lint_and_then(cx, PTR_OFFSET_BY_LITERAL, expr.span, msg, |diag| { + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + format!("remove the call to `{method}`"), + String::new(), + Applicability::MachineApplicable, + ); + }); + }, + Some(method_suggestion) => { + let msg = format!("use of `{method}` with a literal"); + span_lint_and_then(cx, PTR_OFFSET_BY_LITERAL, expr.span, msg, |diag| { + diag.multipart_suggestion( + format!("use `{method_suggestion}` instead"), + vec![ + (method_name.ident.span, method_suggestion.to_string()), + (arg_expr.span, literal_text), + ], + Applicability::MachineApplicable, + ); + }); + }, + } +} + +fn get_literal_bits<'tcx>(expr: &'tcx Expr<'tcx>) -> Option { + match expr.kind { + ExprKind::Lit(Lit { + node: LitKind::Int(packed_u128, _), + .. + }) => Some(packed_u128.get()), + _ => None, + } +} + +// If the given expression is a (negated) literal, return its value. +fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<(i128, String)> { + if let Some(literal_bits) = get_literal_bits(expr) { + // The value must fit in a isize, so we can't have overflow here. + return Some((literal_bits.cast_signed(), format_isize_literal(cx, expr)?)); + } + + if let ExprKind::Unary(UnOp::Neg, inner) = expr.kind + && let Some(literal_bits) = get_literal_bits(inner) + { + return Some((-(literal_bits.cast_signed()), format_isize_literal(cx, inner)?)); + } + + None +} + +fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + let text = expr.span.get_source_text(cx)?; + let text = peel_parens_str(&text); + Some(text.trim_end_matches("isize").trim_end_matches('_').to_string()) +} + +fn peel_parens_str(snippet: &str) -> &str { + let mut s = snippet.trim(); + while let Some(next) = s.strip_prefix("(").and_then(|suf| suf.strip_suffix(")")) { + s = next.trim(); + } + s +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + fn suggestion(self, literal: i128) -> Option<&'static str> { + match Ord::cmp(&literal, &0) { + Ordering::Greater => match self { + Method::Offset => Some("add"), + Method::WrappingOffset => Some("wrapping_add"), + }, + // `ptr.offset(0)` is equivalent to `ptr`, so no adjustment is needed + Ordering::Equal => None, + Ordering::Less => match self { + Method::Offset => Some("sub"), + Method::WrappingOffset => Some("wrapping_sub"), + }, + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index bfe826508f36..a4689a7840ce 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::borrow_as_ptr)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::ptr_offset_by_literal)] extern crate proc_macros; diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index ce248f157c6e..d7468f37a3a4 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::borrow_as_ptr)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::ptr_offset_by_literal)] extern crate proc_macros; diff --git a/tests/ui/crashes/ice-4579.rs b/tests/ui/crashes/ice-4579.rs index 14c8113e315b..a2592e74c66d 100644 --- a/tests/ui/crashes/ice-4579.rs +++ b/tests/ui/crashes/ice-4579.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(clippy::single_match)] +#![allow(clippy::single_match, clippy::ptr_offset_by_literal)] use std::ptr; diff --git a/tests/ui/ptr_offset_by_literal.fixed b/tests/ui/ptr_offset_by_literal.fixed new file mode 100644 index 000000000000..bd9e41def938 --- /dev/null +++ b/tests/ui/ptr_offset_by_literal.fixed @@ -0,0 +1,50 @@ +#![warn(clippy::ptr_offset_by_literal)] +#![allow(clippy::inconsistent_digit_grouping)] + +fn main() { + let arr = [b'a', b'b', b'c']; + let ptr = arr.as_ptr(); + + let var = 32; + const CONST: isize = 42; + + unsafe { + let _ = ptr; + //~^ ptr_offset_by_literal + let _ = ptr; + //~^ ptr_offset_by_literal + + let _ = ptr.add(5); + //~^ ptr_offset_by_literal + let _ = ptr.sub(5); + //~^ ptr_offset_by_literal + + let _ = ptr.offset(var); + let _ = ptr.offset(CONST); + + let _ = ptr.wrapping_add(5); + //~^ ptr_offset_by_literal + let _ = ptr.wrapping_sub(5); + //~^ ptr_offset_by_literal + + let _ = ptr.sub(5); + //~^ ptr_offset_by_literal + let _ = ptr.wrapping_sub(5); + //~^ ptr_offset_by_literal + + // isize::MAX and isize::MIN on 32-bit systems. + let _ = ptr.add(2_147_483_647); + //~^ ptr_offset_by_literal + let _ = ptr.sub(2_147_483_648); + //~^ ptr_offset_by_literal + + let _ = ptr.add(5_0); + //~^ ptr_offset_by_literal + let _ = ptr.sub(5_0); + //~^ ptr_offset_by_literal + + macro_rules! offs { { $e:expr, $offs:expr } => { $e.offset($offs) }; } + offs!(ptr, 6); + offs!(ptr, var); + } +} diff --git a/tests/ui/ptr_offset_by_literal.rs b/tests/ui/ptr_offset_by_literal.rs new file mode 100644 index 000000000000..b8e3f9b26c68 --- /dev/null +++ b/tests/ui/ptr_offset_by_literal.rs @@ -0,0 +1,50 @@ +#![warn(clippy::ptr_offset_by_literal)] +#![allow(clippy::inconsistent_digit_grouping)] + +fn main() { + let arr = [b'a', b'b', b'c']; + let ptr = arr.as_ptr(); + + let var = 32; + const CONST: isize = 42; + + unsafe { + let _ = ptr.offset(0); + //~^ ptr_offset_by_literal + let _ = ptr.offset(-0); + //~^ ptr_offset_by_literal + + let _ = ptr.offset(5); + //~^ ptr_offset_by_literal + let _ = ptr.offset(-5); + //~^ ptr_offset_by_literal + + let _ = ptr.offset(var); + let _ = ptr.offset(CONST); + + let _ = ptr.wrapping_offset(5isize); + //~^ ptr_offset_by_literal + let _ = ptr.wrapping_offset(-5isize); + //~^ ptr_offset_by_literal + + let _ = ptr.offset(-(5)); + //~^ ptr_offset_by_literal + let _ = ptr.wrapping_offset(-(5)); + //~^ ptr_offset_by_literal + + // isize::MAX and isize::MIN on 32-bit systems. + let _ = ptr.offset(2_147_483_647isize); + //~^ ptr_offset_by_literal + let _ = ptr.offset(-2_147_483_648isize); + //~^ ptr_offset_by_literal + + let _ = ptr.offset(5_0__isize); + //~^ ptr_offset_by_literal + let _ = ptr.offset(-5_0__isize); + //~^ ptr_offset_by_literal + + macro_rules! offs { { $e:expr, $offs:expr } => { $e.offset($offs) }; } + offs!(ptr, 6); + offs!(ptr, var); + } +} diff --git a/tests/ui/ptr_offset_by_literal.stderr b/tests/ui/ptr_offset_by_literal.stderr new file mode 100644 index 000000000000..f85fef87d55f --- /dev/null +++ b/tests/ui/ptr_offset_by_literal.stderr @@ -0,0 +1,141 @@ +error: use of `offset` with zero + --> tests/ui/ptr_offset_by_literal.rs:12:17 + | +LL | let _ = ptr.offset(0); + | ^^^---------- + | | + | help: remove the call to `offset` + | + = note: `-D clippy::ptr-offset-by-literal` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_by_literal)]` + +error: use of `offset` with zero + --> tests/ui/ptr_offset_by_literal.rs:14:17 + | +LL | let _ = ptr.offset(-0); + | ^^^----------- + | | + | help: remove the call to `offset` + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:17:17 + | +LL | let _ = ptr.offset(5); + | ^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = ptr.offset(5); +LL + let _ = ptr.add(5); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:19:17 + | +LL | let _ = ptr.offset(-5); + | ^^^^^^^^^^^^^^ + | +help: use `sub` instead + | +LL - let _ = ptr.offset(-5); +LL + let _ = ptr.sub(5); + | + +error: use of `wrapping_offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:25:17 + | +LL | let _ = ptr.wrapping_offset(5isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = ptr.wrapping_offset(5isize); +LL + let _ = ptr.wrapping_add(5); + | + +error: use of `wrapping_offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:27:17 + | +LL | let _ = ptr.wrapping_offset(-5isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_sub` instead + | +LL - let _ = ptr.wrapping_offset(-5isize); +LL + let _ = ptr.wrapping_sub(5); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:30:17 + | +LL | let _ = ptr.offset(-(5)); + | ^^^^^^^^^^^^^^^^ + | +help: use `sub` instead + | +LL - let _ = ptr.offset(-(5)); +LL + let _ = ptr.sub(5); + | + +error: use of `wrapping_offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:32:17 + | +LL | let _ = ptr.wrapping_offset(-(5)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_sub` instead + | +LL - let _ = ptr.wrapping_offset(-(5)); +LL + let _ = ptr.wrapping_sub(5); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:36:17 + | +LL | let _ = ptr.offset(2_147_483_647isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = ptr.offset(2_147_483_647isize); +LL + let _ = ptr.add(2_147_483_647); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:38:17 + | +LL | let _ = ptr.offset(-2_147_483_648isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `sub` instead + | +LL - let _ = ptr.offset(-2_147_483_648isize); +LL + let _ = ptr.sub(2_147_483_648); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:41:17 + | +LL | let _ = ptr.offset(5_0__isize); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = ptr.offset(5_0__isize); +LL + let _ = ptr.add(5_0); + | + +error: use of `offset` with a literal + --> tests/ui/ptr_offset_by_literal.rs:43:17 + | +LL | let _ = ptr.offset(-5_0__isize); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `sub` instead + | +LL - let _ = ptr.offset(-5_0__isize); +LL + let _ = ptr.sub(5_0); + | + +error: aborting due to 12 previous errors + diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs index bedb09536c53..5a9c3ac9248f 100644 --- a/tests/ui/zero_offset.rs +++ b/tests/ui/zero_offset.rs @@ -1,4 +1,4 @@ -#[allow(clippy::borrow_as_ptr)] +#[allow(clippy::borrow_as_ptr, clippy::ptr_offset_by_literal)] fn main() { unsafe { let m = &mut () as *mut (); From d706368b7006146cd43eb1774b79d481cddb0d30 Mon Sep 17 00:00:00 2001 From: Aliaksei Semianiuk Date: Sun, 30 Nov 2025 15:56:32 +0500 Subject: [PATCH 18/56] Changelog for Clippy 1.92 --- CHANGELOG.md | 83 ++++++++++++++++++++++++++++++++- clippy_lints/src/methods/mod.rs | 4 +- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b81b5b74d6..cf619d254cec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,88 @@ document. ## Unreleased / Beta / In Rust Nightly -[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) +[d9fb15c...master](https://github.com/rust-lang/rust-clippy/compare/d9fb15c...master) + +## Rust 1.92 + +Current stable, released 2025-12-11 + +[View all 124 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-09-05T18%3A24%3A03Z..2025-10-16T14%3A13%3A43Z+base%3Amaster) + +### New Lints + +* Added [`unnecessary_option_map_or_else`] to `suspicious` + [#14662](https://github.com/rust-lang/rust-clippy/pull/14662) +* Added [`replace_box`] to `perf` + [#14953](https://github.com/rust-lang/rust-clippy/pull/14953) +* Added [`volatile_composites`] to `nursery` + [#15686](https://github.com/rust-lang/rust-clippy/pull/15686) +* Added [`self_only_used_in_recursion`] to `pedantic` + [#14787](https://github.com/rust-lang/rust-clippy/pull/14787) +* Added [`redundant_iter_cloned`] to `perf` + [#15277](https://github.com/rust-lang/rust-clippy/pull/15277) + +### Moves and Deprecations + +* Renamed [`unchecked_duration_subtraction`] to [`unchecked_time_subtraction`] + [#13800](https://github.com/rust-lang/rust-clippy/pull/13800) + +### Enhancements + +* [`mutex_atomic`] and [`mutex_integer`] overhauled to only lint definitions, not uses; added suggestions + and better help messages + [#15632](https://github.com/rust-lang/rust-clippy/pull/15632) +* [`manual_rotate`] now recognizes non-const rotation amounts + [#15402](https://github.com/rust-lang/rust-clippy/pull/15402) +* [`multiple_inherent_impl`] added `inherent-impl-lint-scope` config option (`module`, `file`, + or `crate`) + [#15843](https://github.com/rust-lang/rust-clippy/pull/15843) +* [`use_self`] now checks structs and enums + [#15566](https://github.com/rust-lang/rust-clippy/pull/15566) +* [`while_let_loop`] extended to lint on `loop { let else }` + [#15701](https://github.com/rust-lang/rust-clippy/pull/15701) +* [`mut_mut`] overhauled with structured suggestions and improved documentation + [#15417](https://github.com/rust-lang/rust-clippy/pull/15417) +* [`nonstandard_macro_braces`] now suggests trailing semicolon when needed + [#15593](https://github.com/rust-lang/rust-clippy/pull/15593) +* [`ptr_offset_with_cast`] now respects MSRV when suggesting fix, and lints more cases + [#15613](https://github.com/rust-lang/rust-clippy/pull/15613) +* [`cast_sign_loss`] and [`cast_possible_wrap`] added suggestions using `cast_{un,}signed()` methods + (MSRV 1.87+) + [#15384](https://github.com/rust-lang/rust-clippy/pull/15384) +* [`unchecked_time_subtraction`] extended to include `Duration - Duration` operations + [#13800](https://github.com/rust-lang/rust-clippy/pull/13800) +* [`filter_next`] now suggests replacing `filter().next_back()` with `rfind()` for + `DoubleEndedIterator` + [#15748](https://github.com/rust-lang/rust-clippy/pull/15748) + +### False Positive Fixes + +* [`unnecessary_safety_comment`] fixed FPs with comments above attributes + [#15678](https://github.com/rust-lang/rust-clippy/pull/15678) +* [`manual_unwrap_or`] fixed FP edge case + [#15812](https://github.com/rust-lang/rust-clippy/pull/15812) +* [`needless_continue`] fixed FP when match type is not unit or never + [#15547](https://github.com/rust-lang/rust-clippy/pull/15547) +* [`if_then_some_else_none`] fixed FP when return exists in block expr + [#15783](https://github.com/rust-lang/rust-clippy/pull/15783) +* [`new_without_default`] fixed to copy `#[cfg]` onto `impl Default` and fixed FP on private type + with trait impl + [#15720](https://github.com/rust-lang/rust-clippy/pull/15720) + [#15782](https://github.com/rust-lang/rust-clippy/pull/15782) +* [`question_mark`] fixed FP on variables used after + [#15644](https://github.com/rust-lang/rust-clippy/pull/15644) +* [`needless_return`] fixed FP with `cfg`d code after `return` + [#15669](https://github.com/rust-lang/rust-clippy/pull/15669) +* [`useless_attribute`] fixed FP on `deprecated_in_future` + [#15645](https://github.com/rust-lang/rust-clippy/pull/15645) +* [`double_parens`] fixed FP when macros are involved + [#15420](https://github.com/rust-lang/rust-clippy/pull/15420) + +### ICE Fixes + +* [`len_zero`] fixed ICE when fn len has a return type without generic type params + [#15660](https://github.com/rust-lang/rust-clippy/pull/15660) ## Rust 1.91 diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c22b0a548e3d..089e32d1e430 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4635,7 +4635,7 @@ declare_clippy_lint! { /// let x = vec![String::new()]; /// let _ = x.iter().map(|x| x.len()); /// ``` - #[clippy::version = "1.90.0"] + #[clippy::version = "1.92.0"] pub REDUNDANT_ITER_CLONED, perf, "detects redundant calls to `Iterator::cloned`" @@ -4659,7 +4659,7 @@ declare_clippy_lint! { /// let x: Option = Some(4); /// let y = x.unwrap_or_else(|| 2 * k); /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.92.0"] pub UNNECESSARY_OPTION_MAP_OR_ELSE, suspicious, "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" From 3e88c6a639ea28ada59ef63f4b11814d6de5c489 Mon Sep 17 00:00:00 2001 From: bendn Date: Tue, 11 Nov 2025 13:46:52 +0700 Subject: [PATCH 19/56] stabilize [T]::array_windows --- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 230d83dacc95..bc62d9b8450c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(array_windows)] +#![cfg_attr(bootstrap, feature(array_windows))] #![feature(box_patterns)] #![feature(macro_metavar_expr_concat)] #![feature(f128)] diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index ed164fcf371b..409f13013489 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -5,7 +5,7 @@ #![feature(rustc_private)] #![feature(assert_matches)] #![feature(unwrap_infallible)] -#![feature(array_windows)] +#![cfg_attr(bootstrap, feature(array_windows))] #![recursion_limit = "512"] #![allow( clippy::missing_errors_doc, From 1b76a346e826930d0b1e4901781da6a3038bac99 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 22 Jun 2025 15:43:06 +0800 Subject: [PATCH 20/56] fix: `large_stack_frames` FP on compiler generated targets --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++ clippy_config/src/conf.rs | 3 + clippy_lints/src/large_stack_frames.rs | 152 ++++++++++++------ .../large_stack_frames_for_macros/clippy.toml | 1 + .../large_stack_frames.rs | 42 +++++ .../large_stack_frames.stderr | 89 ++++++++++ .../clippy.toml | 2 + .../large_stack_frames.rs | 13 ++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + 10 files changed, 269 insertions(+), 47 deletions(-) create mode 100644 tests/ui-toml/large_stack_frames_for_macros/clippy.toml create mode 100644 tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs create mode 100644 tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.stderr create mode 100644 tests/ui-toml/large_stack_frames_for_special_targets/clippy.toml create mode 100644 tests/ui-toml/large_stack_frames_for_special_targets/large_stack_frames.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7db95c081aa3..d7f663ee9f1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7081,6 +7081,7 @@ Released 2018-09-13 [`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests +[`allow-large-stack-frames-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-large-stack-frames-in-tests [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 6569bdabf115..c2e7e19042c7 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -111,6 +111,16 @@ Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]` * [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) +## `allow-large-stack-frames-in-tests` +Whether functions inside `#[cfg(test)]` modules or test functions should be checked. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames) + + ## `allow-mixed-uninlined-format-args` Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2a042e6c3d85..2cec8a1a2821 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -373,6 +373,9 @@ define_Conf! { /// Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]` #[lints(indexing_slicing)] allow_indexing_slicing_in_tests: bool = false, + /// Whether functions inside `#[cfg(test)]` modules or test functions should be checked. + #[lints(large_stack_frames)] + allow_large_stack_frames_in_tests: bool = true, /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` #[lints(uninlined_format_args)] allow_mixed_uninlined_format_args: bool = true, diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 5ed948c02bbc..6b0080d04c44 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -2,15 +2,16 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::fn_has_unsatisfiable_preds; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{HasSession, SpanRangeExt}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; +use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; use rustc_lexer::is_ident; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -83,12 +84,14 @@ declare_clippy_lint! { pub struct LargeStackFrames { maximum_allowed_size: u64, + allow_large_stack_frames_in_tests: bool, } impl LargeStackFrames { pub fn new(conf: &'static Conf) -> Self { Self { maximum_allowed_size: conf.stack_size_threshold, + allow_large_stack_frames_in_tests: conf.allow_large_stack_frames_in_tests, } } } @@ -152,67 +155,122 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { let mir = cx.tcx.optimized_mir(def_id); let typing_env = mir.typing_env(cx.tcx); - let sizes_of_locals = || { - mir.local_decls.iter().filter_map(|local| { + let sizes_of_locals = mir + .local_decls + .iter() + .filter_map(|local| { let layout = cx.tcx.layout_of(typing_env.as_query_input(local.ty)).ok()?; Some((local, layout.size.bytes())) }) - }; + .collect::>(); - let frame_size = sizes_of_locals().fold(Space::Used(0), |sum, (_, size)| sum + size); + let frame_size = sizes_of_locals + .iter() + .fold(Space::Used(0), |sum, (_, size)| sum + *size); let limit = self.maximum_allowed_size; if frame_size.exceeds_limit(limit) { // Point at just the function name if possible, because lints that span // the entire body and don't have to are less legible. - let fn_span = match fn_kind { - FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, - FnKind::Closure => entire_fn_span, + let (fn_span, fn_name) = match fn_kind { + FnKind::ItemFn(ident, _, _) => (ident.span, format!("function `{}`", ident.name)), + FnKind::Method(ident, _) => (ident.span, format!("method `{}`", ident.name)), + FnKind::Closure => (entire_fn_span, "closure".to_string()), }; + // Don't lint inside tests if configured to not do so. + if self.allow_large_stack_frames_in_tests && is_in_test(cx.tcx, cx.tcx.local_def_id_to_hir_id(local_def_id)) + { + return; + } + + let explain_lint = |diag: &mut Diag<'_, ()>, ctxt: SyntaxContext| { + // Point out the largest individual contribution to this size, because + // it is the most likely to be unintentionally large. + if let Some((local, size)) = sizes_of_locals.iter().max_by_key(|&(_, size)| size) + && let local_span = local.source_info.span + && local_span.ctxt() == ctxt + { + let size = Space::Used(*size); // pluralizes for us + let ty = local.ty; + + // TODO: Is there a cleaner, robust way to ask this question? + // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", + // and that doesn't get us the true name in scope rather than the span text either. + if let Some(name) = local_span.get_source_text(cx) + && is_ident(&name) + { + // If the local is an ordinary named variable, + // print its name rather than relying solely on the span. + diag.span_label( + local_span, + format!("`{name}` is the largest part, at {size} for type `{ty}`"), + ); + } else { + diag.span_label( + local_span, + format!("this is the largest part, at {size} for type `{ty}`"), + ); + } + } + + // Explain why we are linting this and not other functions. + diag.note(format!( + "{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}" + )); + + // Explain why the user should care, briefly. + diag.note_once( + "allocating large amounts of stack space can overflow the stack \ + and cause the program to abort", + ); + }; + + if fn_span.from_expansion() { + // Don't lint on the main function generated by `--test` target + if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { + return; + } + + let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map()); + span_lint_and_then( + cx, + LARGE_STACK_FRAMES, + fn_span.source_callsite(), + format!( + "{} generated by this macro may allocate a lot of stack space", + if is_from_external_macro { + cx.tcx.def_descr(local_def_id.into()) + } else { + fn_name.as_str() + } + ), + |diag| { + if is_from_external_macro { + return; + } + + diag.span_label( + fn_span, + format!( + "this {} has a stack frame size of {frame_size}", + cx.tcx.def_descr(local_def_id.into()) + ), + ); + + explain_lint(diag, fn_span.ctxt()); + }, + ); + return; + } + span_lint_and_then( cx, LARGE_STACK_FRAMES, fn_span, format!("this function may allocate {frame_size} on the stack"), |diag| { - // Point out the largest individual contribution to this size, because - // it is the most likely to be unintentionally large. - if let Some((local, size)) = sizes_of_locals().max_by_key(|&(_, size)| size) { - let local_span: Span = local.source_info.span; - let size = Space::Used(size); // pluralizes for us - let ty = local.ty; - - // TODO: Is there a cleaner, robust way to ask this question? - // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", - // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = local_span.get_source_text(cx) - && is_ident(&name) - { - // If the local is an ordinary named variable, - // print its name rather than relying solely on the span. - diag.span_label( - local_span, - format!("`{name}` is the largest part, at {size} for type `{ty}`"), - ); - } else { - diag.span_label( - local_span, - format!("this is the largest part, at {size} for type `{ty}`"), - ); - } - } - - // Explain why we are linting this and not other functions. - diag.note(format!( - "{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}" - )); - - // Explain why the user should care, briefly. - diag.note_once( - "allocating large amounts of stack space can overflow the stack \ - and cause the program to abort", - ); + explain_lint(diag, SyntaxContext::root()); }, ); } diff --git a/tests/ui-toml/large_stack_frames_for_macros/clippy.toml b/tests/ui-toml/large_stack_frames_for_macros/clippy.toml new file mode 100644 index 000000000000..b6fd0e8a0483 --- /dev/null +++ b/tests/ui-toml/large_stack_frames_for_macros/clippy.toml @@ -0,0 +1 @@ +stack-size-threshold = 0 diff --git a/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs b/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs new file mode 100644 index 000000000000..1f6265590f93 --- /dev/null +++ b/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs @@ -0,0 +1,42 @@ +//@ignore-target: i686 +//@normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR" +//@normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR" + +#![warn(clippy::large_stack_frames)] + +extern crate serde; +use serde::{Deserialize, Serialize}; + +struct ArrayDefault([u8; N]); + +macro_rules! mac { + ($name:ident) => { + fn foo() { + let $name = 1; + println!("macro_name called"); + } + + fn bar() { + let $name = ArrayDefault([0; 1000]); + } + }; +} + +mac!(something); +//~^ large_stack_frames +//~| large_stack_frames + +#[derive(Deserialize, Serialize)] +//~^ large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +//~| large_stack_frames +struct S { + a: [u128; 31], +} + +fn main() {} diff --git a/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.stderr new file mode 100644 index 000000000000..bc222f6b1039 --- /dev/null +++ b/tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.stderr @@ -0,0 +1,89 @@ +error: function `foo` generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:25:1 + | +LL | fn foo() { + | --- this function has a stack frame size of 20 bytes +... +LL | mac!(something); + | ^^^^^^^^^^^^^^^ + | + = note: 20 bytes is larger than Clippy's configured `stack-size-threshold` of 0 + = note: allocating large amounts of stack space can overflow the stack and cause the program to abort + = note: `-D clippy::large-stack-frames` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` + +error: function `bar` generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:25:1 + | +LL | fn bar() { + | --- this function has a stack frame size of 2000 bytes +LL | let $name = ArrayDefault([0; 1000]); + | --------- this is the largest part, at 1000 bytes for type `[u8; 1000]` +... +LL | mac!(something); + | ^^^^^^^^^^^^^^^ + | + = note: 2000 bytes is larger than Clippy's configured `stack-size-threshold` of 0 + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: method generated by this macro may allocate a lot of stack space + --> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:23 + | +LL | #[derive(Deserialize, Serialize)] + | ^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui-toml/large_stack_frames_for_special_targets/clippy.toml b/tests/ui-toml/large_stack_frames_for_special_targets/clippy.toml new file mode 100644 index 000000000000..02f3bc02dc4b --- /dev/null +++ b/tests/ui-toml/large_stack_frames_for_special_targets/clippy.toml @@ -0,0 +1,2 @@ +stack-size-threshold = 0 +allow-large-stack-frames-in-tests = false diff --git a/tests/ui-toml/large_stack_frames_for_special_targets/large_stack_frames.rs b/tests/ui-toml/large_stack_frames_for_special_targets/large_stack_frames.rs new file mode 100644 index 000000000000..cc01232ca408 --- /dev/null +++ b/tests/ui-toml/large_stack_frames_for_special_targets/large_stack_frames.rs @@ -0,0 +1,13 @@ +// This test checks if `clippy::large_stack_frames` is working correctly when encountering functions +// generated by special compiling targets like `--test`. +//@compile-flags: --test +//@check-pass + +#![warn(clippy::large_stack_frames)] + +#[cfg(test)] +#[expect(clippy::large_stack_frames)] +mod test { + #[test] + fn main_test() {} +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 2d9503c5ac53..ac449bc82b9d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -9,6 +9,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests + allow-large-stack-frames-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests @@ -106,6 +107,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests + allow-large-stack-frames-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests @@ -203,6 +205,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests + allow-large-stack-frames-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests From 5ac265676a6a62946382a67a32661b94338c15cd Mon Sep 17 00:00:00 2001 From: ceptontech <> Date: Fri, 24 Oct 2025 14:26:04 -0700 Subject: [PATCH 21/56] feat(transmute_ptr_to_ref): Handle a pointer wrapped in a struct Now the program checks for transmutting from a struct containing a single raw pointer to a reference. ```Rust struct Foo(*const i32); fn foo(foo: Foo) -> &i32 { unsafe { transmute(foo) } } ``` changelog: [`transmute_ptr_to_ref`]: now checks for a pointer wrapped in a struct --- clippy_lints/src/transmute/mod.rs | 34 +++++- .../src/transmute/transmute_ptr_to_ref.rs | 3 +- tests/ui/transmute_ptr_to_ref.fixed | 56 ++++++++- tests/ui/transmute_ptr_to_ref.rs | 56 ++++++++- tests/ui/transmute_ptr_to_ref.stderr | 110 ++++++++++++++---- 5 files changed, 230 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index d643f7aea497..839491b082f2 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -18,8 +18,11 @@ mod wrong_transmute; use clippy_config::Conf; use clippy_utils::is_in_const_context; use clippy_utils::msrvs::Msrv; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; @@ -490,6 +493,32 @@ impl Transmute { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv } } + + /// When transmuting, a struct containing a single field works like the field. + /// This function extracts the field type and the expression to get the field. + fn extract_struct_field<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + outer_type: Ty<'tcx>, + outer: &'tcx Expr<'tcx>, + ) -> (Ty<'tcx>, Sugg<'tcx>) { + let mut applicability = Applicability::MachineApplicable; + let outer_sugg = Sugg::hir_with_context(cx, outer, e.span.ctxt(), "..", &mut applicability); + if let ty::Adt(struct_def, struct_args) = *outer_type.kind() + && struct_def.is_struct() + && let mut fields = struct_def.all_fields() + && let Some(first) = fields.next() + && fields.next().is_none() + && first.vis.is_accessible_from(cx.tcx.parent_module(outer.hir_id), cx.tcx) + { + ( + first.ty(cx.tcx, struct_args), + Sugg::NonParen(format!("{}.{}", outer_sugg.maybe_paren(), first.name).into()), + ) + } else { + (outer_type, outer_sugg) + } + } } impl<'tcx> LateLintPass<'tcx> for Transmute { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { @@ -516,11 +545,14 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { return; } + // A struct having a single pointer can be treated like a pointer. + let (from_field_ty, from_field_expr) = Self::extract_struct_field(cx, e, from_ty, arg); + let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) + | transmute_ptr_to_ref::check(cx, e, from_field_ty, to_ty, from_field_expr.clone(), path, self.msrv) | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index e67ab6a73d26..ba107eed6b14 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>( e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, + arg: sugg::Sugg<'_>, path: &'tcx Path<'_>, msrv: Msrv, ) -> bool { @@ -27,7 +27,6 @@ pub(super) fn check<'tcx>( e.span, format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); let (deref, cast) = match mutbl { Mutability::Mut => ("&mut *", "*mut"), Mutability::Not => ("&*", "*const"), diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index c130575df960..8de47031a400 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -55,6 +55,52 @@ fn issue1231() { //~^ transmute_ptr_to_ref } +#[derive(Clone, Copy)] +struct PtrRefNamed<'a> { + ptr: *const &'a u32, +} +#[derive(Clone, Copy)] +struct PtrRef<'a>(*const &'a u32); +#[derive(Clone, Copy)] +struct PtrSliceRef<'a>(*const [&'a str]); +#[derive(Clone, Copy)] +struct PtrSlice(*const [i32]); +#[derive(Clone, Copy)] +struct Ptr(*const u32); +impl std::ops::Add for Ptr { + type Output = Self; + fn add(self, _: Self) -> Self { + self + } +} +mod ptr_mod { + #[derive(Clone, Copy)] + pub struct Ptr(*const u32); +} +fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) { + unsafe { + let _: &i32 = &*(w.0 as *const i32); + //~^ transmute_ptr_to_ref + let _: &u32 = &*w.0; + //~^ transmute_ptr_to_ref + let _: &&u32 = &*x.ptr.cast::<&u32>(); + //~^ transmute_ptr_to_ref + // The field is not accessible. The program should not generate code + // that accesses the field. + let _: &u32 = std::mem::transmute(z); + let _ = &*w.0.cast::(); + //~^ transmute_ptr_to_ref + let _: &[&str] = &*(v.0 as *const [&str]); + //~^ transmute_ptr_to_ref + let _ = &*(u.0 as *const [i32]); + //~^ transmute_ptr_to_ref + let _: &&u32 = &*y.0.cast::<&u32>(); + //~^ transmute_ptr_to_ref + let _: &u32 = &*(w + w).0; + //~^ transmute_ptr_to_ref + } +} + fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { @@ -89,7 +135,7 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -97,10 +143,16 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { //~^ transmute_ptr_to_ref let _: &u32 = &*(a as *const u32); //~^ transmute_ptr_to_ref + let _ = &*(Ptr(a).0 as *const u32); + //~^ transmute_ptr_to_ref match 0 { 0 => &*(x as *const () as *const &u32), //~^ transmute_ptr_to_ref - _ => &*(x as *const () as *const &'b u32), + 1 => &*(x as *const () as *const &'b u32), + //~^ transmute_ptr_to_ref + 2 => &*(y.0 as *const () as *const &u32), + //~^ transmute_ptr_to_ref + _ => &*(y.0 as *const () as *const &'b u32), //~^ transmute_ptr_to_ref } } diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index f79d54234a2c..52fe669de935 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -55,6 +55,52 @@ fn issue1231() { //~^ transmute_ptr_to_ref } +#[derive(Clone, Copy)] +struct PtrRefNamed<'a> { + ptr: *const &'a u32, +} +#[derive(Clone, Copy)] +struct PtrRef<'a>(*const &'a u32); +#[derive(Clone, Copy)] +struct PtrSliceRef<'a>(*const [&'a str]); +#[derive(Clone, Copy)] +struct PtrSlice(*const [i32]); +#[derive(Clone, Copy)] +struct Ptr(*const u32); +impl std::ops::Add for Ptr { + type Output = Self; + fn add(self, _: Self) -> Self { + self + } +} +mod ptr_mod { + #[derive(Clone, Copy)] + pub struct Ptr(*const u32); +} +fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) { + unsafe { + let _: &i32 = std::mem::transmute(w); + //~^ transmute_ptr_to_ref + let _: &u32 = std::mem::transmute(w); + //~^ transmute_ptr_to_ref + let _: &&u32 = core::mem::transmute(x); + //~^ transmute_ptr_to_ref + // The field is not accessible. The program should not generate code + // that accesses the field. + let _: &u32 = std::mem::transmute(z); + let _ = std::mem::transmute::<_, &u32>(w); + //~^ transmute_ptr_to_ref + let _: &[&str] = core::mem::transmute(v); + //~^ transmute_ptr_to_ref + let _ = std::mem::transmute::<_, &[i32]>(u); + //~^ transmute_ptr_to_ref + let _: &&u32 = std::mem::transmute(y); + //~^ transmute_ptr_to_ref + let _: &u32 = std::mem::transmute(w + w); + //~^ transmute_ptr_to_ref + } +} + fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { @@ -89,7 +135,7 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -97,10 +143,16 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { //~^ transmute_ptr_to_ref let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + let _ = std::mem::transmute::<_, &u32>(Ptr(a)); + //~^ transmute_ptr_to_ref match 0 { 0 => std::mem::transmute(x), //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + 1 => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + 2 => std::mem::transmute(y), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(y), //~^ transmute_ptr_to_ref } } diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 3f404d295fef..c0f0ca916761 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -61,125 +61,191 @@ error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` +error: transmute from a pointer type (`*const u32`) to a reference type (`&i32`) + --> tests/ui/transmute_ptr_to_ref.rs:82:23 + | +LL | let _: &i32 = std::mem::transmute(w); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(w.0 as *const i32)` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:84:23 + | +LL | let _: &u32 = std::mem::transmute(w); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*w.0` + error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:61:18 + --> tests/ui/transmute_ptr_to_ref.rs:86:24 + | +LL | let _: &&u32 = core::mem::transmute(x); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.ptr.cast::<&u32>()` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:91:17 + | +LL | let _ = std::mem::transmute::<_, &u32>(w); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*w.0.cast::()` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:93:26 + | +LL | let _: &[&str] = core::mem::transmute(v); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(v.0 as *const [&str])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:95:17 + | +LL | let _ = std::mem::transmute::<_, &[i32]>(u); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u.0 as *const [i32])` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:97:24 + | +LL | let _: &&u32 = std::mem::transmute(y); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.0.cast::<&u32>()` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:99:23 + | +LL | let _: &u32 = std::mem::transmute(w + w); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(w + w).0` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:107:18 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:63:18 + --> tests/ui/transmute_ptr_to_ref.rs:109:18 | LL | 1 => std::mem::transmute(y), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:65:18 + --> tests/ui/transmute_ptr_to_ref.rs:111:18 | LL | 2 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:67:18 + --> tests/ui/transmute_ptr_to_ref.rs:113:18 | LL | _ => std::mem::transmute::<_, &&'b u32>(y), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:78:23 + --> tests/ui/transmute_ptr_to_ref.rs:124:23 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:80:23 + --> tests/ui/transmute_ptr_to_ref.rs:126:23 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:83:18 + --> tests/ui/transmute_ptr_to_ref.rs:129:18 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:85:18 + --> tests/ui/transmute_ptr_to_ref.rs:131:18 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:96:23 + --> tests/ui/transmute_ptr_to_ref.rs:142:23 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:98:23 + --> tests/ui/transmute_ptr_to_ref.rs:144:23 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:146:17 + | +LL | let _ = std::mem::transmute::<_, &u32>(Ptr(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(Ptr(a).0 as *const u32)` + error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:101:18 + --> tests/ui/transmute_ptr_to_ref.rs:149:18 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:103:18 + --> tests/ui/transmute_ptr_to_ref.rs:151:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), +LL | 1 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:153:18 + | +LL | 2 => std::mem::transmute(y), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(y.0 as *const () as *const &u32)` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> tests/ui/transmute_ptr_to_ref.rs:155:18 + | +LL | _ => std::mem::transmute::<_, &&'b u32>(y), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(y.0 as *const () as *const &'b u32)` + error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) - --> tests/ui/transmute_ptr_to_ref.rs:113:17 + --> tests/ui/transmute_ptr_to_ref.rs:165:17 | LL | let _ = core::mem::transmute::<_, &[u32]>(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) - --> tests/ui/transmute_ptr_to_ref.rs:115:25 + --> tests/ui/transmute_ptr_to_ref.rs:167:25 | LL | let _: &[u32] = core::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) - --> tests/ui/transmute_ptr_to_ref.rs:119:17 + --> tests/ui/transmute_ptr_to_ref.rs:171:17 | LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) - --> tests/ui/transmute_ptr_to_ref.rs:121:27 + --> tests/ui/transmute_ptr_to_ref.rs:173:27 | LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) - --> tests/ui/transmute_ptr_to_ref.rs:125:17 + --> tests/ui/transmute_ptr_to_ref.rs:177:17 | LL | let _ = core::mem::transmute::<_, &[i32]>(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])` error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) - --> tests/ui/transmute_ptr_to_ref.rs:127:25 + --> tests/ui/transmute_ptr_to_ref.rs:179:25 | LL | let _: &[i32] = core::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr` error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) - --> tests/ui/transmute_ptr_to_ref.rs:131:17 + --> tests/ui/transmute_ptr_to_ref.rs:183:17 | LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) - --> tests/ui/transmute_ptr_to_ref.rs:133:26 + --> tests/ui/transmute_ptr_to_ref.rs:185:26 | LL | let _: &[&str] = core::mem::transmute(a_s_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` -error: aborting due to 30 previous errors +error: aborting due to 41 previous errors From e9ceae44b7a659f81144ddb175d6bfa4e9b19504 Mon Sep 17 00:00:00 2001 From: ceptontech <> Date: Mon, 1 Dec 2025 09:00:29 -0800 Subject: [PATCH 22/56] feat(transmute_ptr_to_ptr): Handle a pointer wrapped in a struct Now the program checks for transmutting from a struct containing a single raw pointer to a raw pointer. changelog: [`transmute_ptr_to_ptr`]: now checks for a pointer wrapped in a struct --- clippy_lints/src/transmute/mod.rs | 2 +- .../src/transmute/transmute_ptr_to_ptr.rs | 10 +-- tests/ui/transmute_ptr_to_ptr.fixed | 23 ++++++ tests/ui/transmute_ptr_to_ptr.rs | 23 ++++++ tests/ui/transmute_ptr_to_ptr.stderr | 82 +++++++++++++++---- 5 files changed, 116 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 839491b082f2..435cd7bcba4e 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -555,7 +555,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_ptr_to_ref::check(cx, e, from_field_ty, to_ty, from_field_expr.clone(), path, self.msrv) | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) + | transmute_ptr_to_ptr::check(cx, e, from_field_ty, to_ty, from_field_expr, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 91fce5d5bd68..036b16e3dc3d 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -14,11 +14,9 @@ pub(super) fn check<'tcx>( e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, + arg: sugg::Sugg<'_>, msrv: Msrv, ) -> bool { - let mut applicability = Applicability::MachineApplicable; - let arg_sugg = sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut applicability); match (from_ty.kind(), to_ty.kind()) { (ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => { span_lint_and_then( @@ -34,7 +32,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, "use `pointer::cast` instead", - format!("{}.cast::<{to_pointee_ty}>()", arg_sugg.maybe_paren()), + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else if from_pointee_ty == to_pointee_ty @@ -49,14 +47,14 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, format!("use `pointer::{method}` instead"), - format!("{}.{method}()", arg_sugg.maybe_paren()), + format!("{}.{method}()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else { diag.span_suggestion_verbose( e.span, "use an `as` cast instead", - arg_sugg.as_ty(to_ty), + arg.as_ty(to_ty), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/transmute_ptr_to_ptr.fixed b/tests/ui/transmute_ptr_to_ptr.fixed index 476e7e35a1f6..caba277db754 100644 --- a/tests/ui/transmute_ptr_to_ptr.fixed +++ b/tests/ui/transmute_ptr_to_ptr.fixed @@ -24,6 +24,13 @@ struct GenericParam { t: T, } +#[derive(Clone, Copy)] +struct PtrNamed { + ptr: *const u32, +} +#[derive(Clone, Copy)] +struct Ptr(*const u32); + fn transmute_ptr_to_ptr() { let ptr = &1u32 as *const u32; let mut_ptr = &mut 1u32 as *mut u32; @@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) }; } +fn issue1966() { + let ptr = &1u32 as *const u32; + unsafe { + let _: *const f32 = Ptr(ptr).0.cast::(); + //~^ transmute_ptr_to_ptr + let _: *const f32 = PtrNamed { ptr }.ptr.cast::(); + //~^ transmute_ptr_to_ptr + let _: *mut u32 = Ptr(ptr).0.cast_mut(); + //~^ transmute_ptr_to_ptr + } +} + fn lifetime_to_static(v: *mut &()) -> *const &'static () { unsafe { v as *const &() } //~^ transmute_ptr_to_ptr @@ -81,11 +100,15 @@ const _: &() = { unsafe { transmute::<&'static Zst, &'static ()>(zst) } }; +#[derive(Clone, Copy)] +struct Ptr8(*const u8); #[clippy::msrv = "1.37"] fn msrv_1_37(ptr: *const u8) { unsafe { let _: *const i8 = ptr as *const i8; //~^ transmute_ptr_to_ptr + let _: *const i8 = Ptr8(ptr).0 as *const i8; + //~^ transmute_ptr_to_ptr } } diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 7356668bcab5..b3c2baf29c36 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -24,6 +24,13 @@ struct GenericParam { t: T, } +#[derive(Clone, Copy)] +struct PtrNamed { + ptr: *const u32, +} +#[derive(Clone, Copy)] +struct Ptr(*const u32); + fn transmute_ptr_to_ptr() { let ptr = &1u32 as *const u32; let mut_ptr = &mut 1u32 as *mut u32; @@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) }; } +fn issue1966() { + let ptr = &1u32 as *const u32; + unsafe { + let _: *const f32 = transmute(Ptr(ptr)); + //~^ transmute_ptr_to_ptr + let _: *const f32 = transmute(PtrNamed { ptr }); + //~^ transmute_ptr_to_ptr + let _: *mut u32 = transmute(Ptr(ptr)); + //~^ transmute_ptr_to_ptr + } +} + fn lifetime_to_static(v: *mut &()) -> *const &'static () { unsafe { transmute(v) } //~^ transmute_ptr_to_ptr @@ -81,11 +100,15 @@ const _: &() = { unsafe { transmute::<&'static Zst, &'static ()>(zst) } }; +#[derive(Clone, Copy)] +struct Ptr8(*const u8); #[clippy::msrv = "1.37"] fn msrv_1_37(ptr: *const u8) { unsafe { let _: *const i8 = transmute(ptr); //~^ transmute_ptr_to_ptr + let _: *const i8 = transmute(Ptr8(ptr)); + //~^ transmute_ptr_to_ptr } } diff --git a/tests/ui/transmute_ptr_to_ptr.stderr b/tests/ui/transmute_ptr_to_ptr.stderr index c8db4fe214fd..ba9e6df6c2d7 100644 --- a/tests/ui/transmute_ptr_to_ptr.stderr +++ b/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:32:29 + --> tests/ui/transmute_ptr_to_ptr.rs:39:29 | LL | let _: *const f32 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let _: *const f32 = ptr.cast::(); | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:35:27 + --> tests/ui/transmute_ptr_to_ptr.rs:42:27 | LL | let _: *mut f32 = transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^ @@ -25,37 +25,37 @@ LL + let _: *mut f32 = mut_ptr.cast::(); | error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:39:23 + --> tests/ui/transmute_ptr_to_ptr.rs:46:23 | LL | let _: &f32 = transmute(&1u32); | ^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:42:23 + --> tests/ui/transmute_ptr_to_ptr.rs:49:23 | LL | let _: &f32 = transmute(&1f64); | ^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:47:27 + --> tests/ui/transmute_ptr_to_ptr.rs:54:27 | LL | let _: &mut f32 = transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:50:37 + --> tests/ui/transmute_ptr_to_ptr.rs:57:37 | LL | let _: &GenericParam = transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:54:27 + --> tests/ui/transmute_ptr_to_ptr.rs:61:27 | LL | let u8_ref: &u8 = transmute(u64_ref); | ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)` error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:57:29 + --> tests/ui/transmute_ptr_to_ptr.rs:64:29 | LL | let _: *const u32 = transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL + let _: *const u32 = mut_ptr.cast_const(); | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:60:27 + --> tests/ui/transmute_ptr_to_ptr.rs:67:27 | LL | let _: *mut u32 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -79,7 +79,43 @@ LL + let _: *mut u32 = ptr.cast_mut(); | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:72:14 + --> tests/ui/transmute_ptr_to_ptr.rs:81:29 + | +LL | let _: *const f32 = transmute(Ptr(ptr)); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast` instead + | +LL - let _: *const f32 = transmute(Ptr(ptr)); +LL + let _: *const f32 = Ptr(ptr).0.cast::(); + | + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:83:29 + | +LL | let _: *const f32 = transmute(PtrNamed { ptr }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast` instead + | +LL - let _: *const f32 = transmute(PtrNamed { ptr }); +LL + let _: *const f32 = PtrNamed { ptr }.ptr.cast::(); + | + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:85:27 + | +LL | let _: *mut u32 = transmute(Ptr(ptr)); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast_mut` instead + | +LL - let _: *mut u32 = transmute(Ptr(ptr)); +LL + let _: *mut u32 = Ptr(ptr).0.cast_mut(); + | + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:91:14 | LL | unsafe { transmute(v) } | ^^^^^^^^^^^^ @@ -91,7 +127,7 @@ LL + unsafe { v as *const &() } | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:87:28 + --> tests/ui/transmute_ptr_to_ptr.rs:108:28 | LL | let _: *const i8 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -103,7 +139,19 @@ LL + let _: *const i8 = ptr as *const i8; | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:95:28 + --> tests/ui/transmute_ptr_to_ptr.rs:110:28 + | +LL | let _: *const i8 = transmute(Ptr8(ptr)); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL - let _: *const i8 = transmute(Ptr8(ptr)); +LL + let _: *const i8 = Ptr8(ptr).0 as *const i8; + | + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:118:28 | LL | let _: *const i8 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -115,7 +163,7 @@ LL + let _: *const i8 = ptr.cast::(); | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:103:26 + --> tests/ui/transmute_ptr_to_ptr.rs:126:26 | LL | let _: *mut u8 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -127,7 +175,7 @@ LL + let _: *mut u8 = ptr as *mut u8; | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:105:28 + --> tests/ui/transmute_ptr_to_ptr.rs:128:28 | LL | let _: *const u8 = transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^ @@ -139,7 +187,7 @@ LL + let _: *const u8 = mut_ptr as *const u8; | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:113:26 + --> tests/ui/transmute_ptr_to_ptr.rs:136:26 | LL | let _: *mut u8 = transmute(ptr); | ^^^^^^^^^^^^^^ @@ -151,7 +199,7 @@ LL + let _: *mut u8 = ptr.cast_mut(); | error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:115:28 + --> tests/ui/transmute_ptr_to_ptr.rs:138:28 | LL | let _: *const u8 = transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^ @@ -162,5 +210,5 @@ LL - let _: *const u8 = transmute(mut_ptr); LL + let _: *const u8 = mut_ptr.cast_const(); | -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors From e4b8c5a6a5d52532e8bb23196f1b0e3d1b9f90e4 Mon Sep 17 00:00:00 2001 From: Linshu Yang Date: Mon, 1 Dec 2025 22:27:51 +0000 Subject: [PATCH 23/56] fix: `useless_conversion` wrongly unmangled macros --- clippy_lints/src/useless_conversion.rs | 5 ++++- tests/ui/useless_conversion.fixed | 11 +++++++++++ tests/ui/useless_conversion.rs | 11 +++++++++++ tests/ui/useless_conversion.stderr | 8 +++++++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 0cf5b9431a34..c06313d1a4c4 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -354,7 +354,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { return; } - let sugg = snippet(cx, recv.span, "").into_owned(); + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut applicability) + .0 + .into_owned(); span_lint_and_sugg( cx, USELESS_CONVERSION, diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 9de7d2c67149..adf5e58d9a1a 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -442,3 +442,14 @@ fn issue14739() { let _ = R.map(|_x| 0); //~^ useless_conversion } + +fn issue16165() { + macro_rules! mac { + (iter $e:expr) => { + $e.iter() + }; + } + + for _ in mac!(iter [1, 2]) {} + //~^ useless_conversion +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 38cd1175aa48..d95fe49e2e2b 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -442,3 +442,14 @@ fn issue14739() { let _ = R.into_iter().map(|_x| 0); //~^ useless_conversion } + +fn issue16165() { + macro_rules! mac { + (iter $e:expr) => { + $e.iter() + }; + } + + for _ in mac!(iter [1, 2]).into_iter() {} + //~^ useless_conversion +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 3bfaf1411c2c..052c664f6f2e 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -389,5 +389,11 @@ error: useless conversion to the same type: `std::ops::Range` LL | let _ = R.into_iter().map(|_x| 0); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` -error: aborting due to 43 previous errors +error: useless conversion to the same type: `std::slice::Iter<'_, i32>` + --> tests/ui/useless_conversion.rs:453:14 + | +LL | for _ in mac!(iter [1, 2]).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])` + +error: aborting due to 44 previous errors From b07013c4fdfc0bb68044e4f39ab66d4f53e40dd9 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Mon, 1 Dec 2025 07:21:47 -0700 Subject: [PATCH 24/56] chore: Update annotate-snippets to 0.12.10 --- tests/ui/manual_async_fn.stderr | 36 +++++++++++++++++++ tests/ui/map_unwrap_or.stderr | 4 +++ tests/ui/match_same_arms.stderr | 1 - .../ui/non_canonical_partial_ord_impl.stderr | 2 ++ tests/ui/print_literal.stderr | 1 + 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 54a9b1d40a11..fe6a20589b96 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -9,6 +9,9 @@ LL | fn fut() -> impl Future { help: make the function `async` and return the output of the future directly | LL - fn fut() -> impl Future { +LL - +LL - async { 42 } +LL - } LL + async fn fut() -> i32 { 42 } | @@ -21,6 +24,9 @@ LL | fn fut2() ->impl Future { help: make the function `async` and return the output of the future directly | LL - fn fut2() ->impl Future { +LL - +LL - async { 42 } +LL - } LL + async fn fut2() -> i32 { 42 } | @@ -33,6 +39,9 @@ LL | fn fut3()-> impl Future { help: make the function `async` and return the output of the future directly | LL - fn fut3()-> impl Future { +LL - +LL - async { 42 } +LL - } LL + async fn fut3() -> i32 { 42 } | @@ -45,6 +54,9 @@ LL | fn empty_fut() -> impl Future { help: make the function `async` and return the output of the future directly | LL - fn empty_fut() -> impl Future { +LL - +LL - async {} +LL - } LL + async fn empty_fut() {} | @@ -57,6 +69,9 @@ LL | fn empty_fut2() ->impl Future { help: make the function `async` and return the output of the future directly | LL - fn empty_fut2() ->impl Future { +LL - +LL - async {} +LL - } LL + async fn empty_fut2() {} | @@ -69,6 +84,9 @@ LL | fn empty_fut3()-> impl Future { help: make the function `async` and return the output of the future directly | LL - fn empty_fut3()-> impl Future { +LL - +LL - async {} +LL - } LL + async fn empty_fut3() {} | @@ -81,6 +99,9 @@ LL | fn core_fut() -> impl core::future::Future { help: make the function `async` and return the output of the future directly | LL - fn core_fut() -> impl core::future::Future { +LL - +LL - async move { 42 } +LL - } LL + async fn core_fut() -> i32 { 42 } | @@ -116,6 +137,9 @@ LL | fn elided(_: &i32) -> impl Future + '_ { help: make the function `async` and return the output of the future directly | LL - fn elided(_: &i32) -> impl Future + '_ { +LL - +LL - async { 42 } +LL - } LL + async fn elided(_: &i32) -> i32 { 42 } | @@ -128,6 +152,9 @@ LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + help: make the function `async` and return the output of the future directly | LL - fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { +LL - +LL - async { 42 } +LL - } LL + async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } | @@ -140,6 +167,9 @@ LL | pub fn issue_10450() -> impl Future { help: make the function `async` and return the output of the future directly | LL - pub fn issue_10450() -> impl Future { +LL - +LL - async { 42 } +LL - } LL + pub async fn issue_10450() -> i32 { 42 } | @@ -152,6 +182,9 @@ LL | pub(crate) fn issue_10450_2() -> impl Future { help: make the function `async` and return the output of the future directly | LL - pub(crate) fn issue_10450_2() -> impl Future { +LL - +LL - async { 42 } +LL - } LL + pub(crate) async fn issue_10450_2() -> i32 { 42 } | @@ -164,6 +197,9 @@ LL | pub(self) fn issue_10450_3() -> impl Future { help: make the function `async` and return the output of the future directly | LL - pub(self) fn issue_10450_3() -> impl Future { +LL - +LL - async { 42 } +LL - } LL + pub(self) async fn issue_10450_3() -> i32 { 42 } | diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 0b6c9b7fcf19..b0b02f3f8d6b 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -12,6 +12,9 @@ LL | | .unwrap_or(0); help: use `map_or(, )` instead | LL - let _ = opt.map(|x| x + 1) +LL - +LL - // Should lint even though this call is on a separate line. +LL - .unwrap_or(0); LL + let _ = opt.map_or(0, |x| x + 1); | @@ -98,6 +101,7 @@ LL | | .unwrap_or(None); help: use `and_then()` instead | LL - .map(|x| Some(x + 1)) +LL - .unwrap_or(None); LL + .and_then(|x| Some(x + 1)); | diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 8aa60f835766..bd0b7b2871ec 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -30,7 +30,6 @@ help: otherwise remove the non-wildcard arms | LL - 2 => 'b', LL - 3 => 'b', -LL + _ => 'b', | error: these match arms have identical bodies diff --git a/tests/ui/non_canonical_partial_ord_impl.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr index 8e55603dd9da..a134df17691e 100644 --- a/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -28,6 +28,8 @@ LL | | } help: change this to | LL - fn partial_cmp(&self, _: &Self) -> Option { +LL - todo!(); +LL - } LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } | diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index c136f52800f6..229f3bf269d3 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -310,6 +310,7 @@ LL | "name", 5, "x", 0.01 help: try | LL - "Hello {}: {2} is {3:.*} (which {3} with {1} places)", +LL - "name", 5, "x", 0.01 LL + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 | From 9c0cbe98ea1890eb261e63ef1cde58f89ee7df38 Mon Sep 17 00:00:00 2001 From: xonx4l Date: Sat, 8 Nov 2025 00:11:35 +0530 Subject: [PATCH 25/56] Merge E0412 into E0425 --- tests/ui/crashes/ice-6252.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr index 201a897b2319..b6a2597a44d5 100644 --- a/tests/ui/crashes/ice-6252.stderr +++ b/tests/ui/crashes/ice-6252.stderr @@ -1,4 +1,4 @@ -error[E0412]: cannot find type `PhantomData` in this scope +error[E0425]: cannot find type `PhantomData` in this scope --> tests/ui/crashes/ice-6252.rs:9:9 | LL | _n: PhantomData, @@ -9,7 +9,7 @@ help: consider importing this struct LL + use std::marker::PhantomData; | -error[E0412]: cannot find type `VAL` in this scope +error[E0425]: cannot find type `VAL` in this scope --> tests/ui/crashes/ice-6252.rs:12:63 | LL | impl TypeVal for Multiply where N: TypeVal {} @@ -31,5 +31,5 @@ LL | impl TypeVal for Multiply where N: TypeVal {} error: aborting due to 3 previous errors -Some errors have detailed explanations: E0046, E0412. +Some errors have detailed explanations: E0046, E0425. For more information about an error, try `rustc --explain E0046`. From f2af204d17aad5264372218c58dd7d87a6769307 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Fri, 10 Oct 2025 00:16:12 +0800 Subject: [PATCH 26/56] fix: `zero_repeat_side_effects` misses curlies --- clippy_lints/src/zero_repeat_side_effects.rs | 72 ++++++++++++++++---- tests/ui/zero_repeat_side_effects.fixed | 32 ++++++++- tests/ui/zero_repeat_side_effects.rs | 26 ++++++- tests/ui/zero_repeat_side_effects.stderr | 52 ++++++++++---- 4 files changed, 151 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index a8351690068d..95085161c09c 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -4,10 +4,11 @@ use clippy_utils::source::{snippet, snippet_indent}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{ConstArgKind, ExprKind, Node}; +use rustc_hir::{ConstArgKind, Expr, ExprKind, LetStmt, LocalSource, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::IsSuggestable; +use rustc_middle::ty::{IsSuggestable, Ty}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -44,7 +45,7 @@ declare_clippy_lint! { declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]); impl LateLintPass<'_> for ZeroRepeatSideEffects { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(args) = VecArgs::hir(cx, expr) && let VecArgs::Repeat(inner_expr, len) = args && let ExprKind::Lit(l) = len.kind @@ -69,7 +70,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { } } -fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { +fn inner_check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, inner_expr: &'_ Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); @@ -81,19 +82,22 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let vec = if is_vec { "vec!" } else { "" }; let (span, sugg) = match parent_hir_node { - Node::LetStmt(l) => ( - l.span, - format!( - "{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];", - var_name = snippet(cx, l.pat.span.source_callsite(), "..") - ), - ), + Node::LetStmt(l) + if matches!(l.source, LocalSource::AssignDesugar) + && let mut parent_iter = cx.tcx.hir_parent_iter(l.hir_id) + && let Some((_, Node::Stmt(_))) = parent_iter.next() + && let Some((_, Node::Block(_))) = parent_iter.next() + && let Some((_, Node::Expr(x))) = parent_iter.next() => + { + ( + x.span, + assign_expr_suggestion(cx, x, l.pat.span, &inner_expr, return_type, vec), + ) + }, + Node::LetStmt(l) => (l.span, let_stmt_suggestion(cx, l, &inner_expr, return_type, vec)), Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( x.span, - format!( - "{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}", - var_name = snippet(cx, l.span.source_callsite(), "..") - ), + assign_expr_suggestion(cx, x, l.span, &inner_expr, return_type, vec), ), // NOTE: don't use the stmt span to avoid touching the trailing semicolon Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")), @@ -131,3 +135,41 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: ); } } + +fn let_stmt_suggestion( + cx: &LateContext<'_>, + let_stmt: &LetStmt<'_>, + inner_expr: &str, + return_type: Ty<'_>, + vec_str: &str, +) -> String { + let indent = snippet_indent(cx, let_stmt.span).unwrap_or_default(); + format!( + "{inner_expr};\n{}let {var_name}: {return_type} = {vec_str}[];", + indent, + var_name = snippet(cx, let_stmt.pat.span.source_callsite(), "..") + ) +} + +fn assign_expr_suggestion( + cx: &LateContext<'_>, + outer_expr: &Expr<'_>, + assign_expr_span: Span, + inner_expr: &str, + return_type: Ty<'_>, + vec_str: &str, +) -> String { + let mut parent_hir_node = cx.tcx.parent_hir_node(outer_expr.hir_id); + if let Node::Stmt(stmt) = parent_hir_node { + parent_hir_node = cx.tcx.parent_hir_node(stmt.hir_id); + } + let needs_curly = !matches!(parent_hir_node, Node::Block(_)); + + let indent = snippet_indent(cx, outer_expr.span).unwrap_or_default(); + let var_name = snippet(cx, assign_expr_span.source_callsite(), ".."); + if needs_curly { + format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}",) + } else { + format!("{inner_expr};\n{indent}{var_name} = {vec_str}[] as {return_type}") + } +} diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index b5fca36f3f08..4bdff6fd0f6c 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,6 +1,11 @@ #![warn(clippy::zero_repeat_side_effects)] -#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] -#![allow(clippy::no_effect)] // only fires _after_ the fix +#![allow( + clippy::unnecessary_operation, + clippy::useless_vec, + clippy::needless_late_init, + clippy::single_match, + clippy::no_effect // only fires _after_ the fix +)] fn f() -> i32 { println!("side effect"); @@ -119,3 +124,26 @@ fn issue_14681() { }); //~^ zero_repeat_side_effects } + +fn issue_15824() { + fn f() {} + + match 0 { + 0 => { + f(); + _ = [] as [(); 0] + }, + //~^ zero_repeat_side_effects + _ => {}, + } + + let mut a = [(); 0]; + match 0 { + 0 => { + f(); + a = [] as [(); 0] + }, + //~^ zero_repeat_side_effects + _ => {}, + } +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index ea043d21638c..a1454d724c87 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,6 +1,11 @@ #![warn(clippy::zero_repeat_side_effects)] -#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] -#![allow(clippy::no_effect)] // only fires _after_ the fix +#![allow( + clippy::unnecessary_operation, + clippy::useless_vec, + clippy::needless_late_init, + clippy::single_match, + clippy::no_effect // only fires _after_ the fix +)] fn f() -> i32 { println!("side effect"); @@ -102,3 +107,20 @@ fn issue_14681() { foo(&[Some(Some(S::new())); 0]); //~^ zero_repeat_side_effects } + +fn issue_15824() { + fn f() {} + + match 0 { + 0 => _ = [f(); 0], + //~^ zero_repeat_side_effects + _ => {}, + } + + let mut a = [(); 0]; + match 0 { + 0 => a = [f(); 0], + //~^ zero_repeat_side_effects + _ => {}, + } +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 49e850d03534..f376a1501b00 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,5 +1,5 @@ error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:17:5 + --> tests/ui/zero_repeat_side_effects.rs:22:5 | LL | let a = [f(); 0]; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:20:5 + --> tests/ui/zero_repeat_side_effects.rs:25:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:25:5 + --> tests/ui/zero_repeat_side_effects.rs:30:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:28:5 + --> tests/ui/zero_repeat_side_effects.rs:33:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:32:5 + --> tests/ui/zero_repeat_side_effects.rs:37:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:36:5 + --> tests/ui/zero_repeat_side_effects.rs:41:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:40:10 + --> tests/ui/zero_repeat_side_effects.rs:45:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:44:5 + --> tests/ui/zero_repeat_side_effects.rs:49:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL ~ vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:46:5 + --> tests/ui/zero_repeat_side_effects.rs:51:5 | LL | [f(); 0]; | ^^^^^^^^ @@ -111,7 +111,7 @@ LL ~ [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:100:10 + --> tests/ui/zero_repeat_side_effects.rs:105:10 | LL | foo(&[Some(f()); 0]); | ^^^^^^^^^^^^^^ @@ -125,7 +125,7 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:102:10 + --> tests/ui/zero_repeat_side_effects.rs:107:10 | LL | foo(&[Some(Some(S::new())); 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,5 +138,33 @@ LL + [] as [std::option::Option>; 0] LL ~ }); | -error: aborting due to 11 previous errors +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:115:14 + | +LL | 0 => _ = [f(); 0], + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL ~ 0 => { +LL + f(); +LL + _ = [] as [(); 0] +LL ~ }, + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:122:14 + | +LL | 0 => a = [f(); 0], + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL ~ 0 => { +LL + f(); +LL + a = [] as [(); 0] +LL ~ }, + | + +error: aborting due to 13 previous errors From 7f61da9c69e5b88fadf3efc789a62c12ea751b2c Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Tue, 28 Oct 2025 17:16:51 +0000 Subject: [PATCH 27/56] account for safe target features in fndef<->closure and fndef<->fndef coerce-lubs --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 1fc8e86f3193..462cc644d4be 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -150,7 +150,7 @@ fn check_rvalue<'tcx>( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer(_), _, ), _, From 298d13e9d0caacd05d722a902eefa5e495f4db40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 3 Dec 2025 16:22:57 +0100 Subject: [PATCH 28/56] Revert "fixup warnings around the compiler" This reverts commit f20175293aa8372766250e56e2570f3c06640e0b. --- clippy_lints/src/manual_non_exhaustive.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 08e7c7593cb2..0d783fde3313 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -92,8 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir_attrs(v.hir_id))) .then_some((v.def_id, v.span)) }); - // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 - if let Ok((id, span)) = Itertools::exactly_one(iter) + if let Ok((id, span)) = iter.exactly_one() && !find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(..)) { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); @@ -105,8 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { .iter() .filter(|field| !cx.effective_visibilities.is_exported(field.def_id)); if fields.len() > 1 - // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 - && let Ok(field) = Itertools::exactly_one(private_fields) + && let Ok(field) = private_fields.exactly_one() && let TyKind::Tup([]) = field.ty.kind { span_lint_and_then( From 680a336fe53ee10ae28b6b9cd9c0669c6d5ca0f6 Mon Sep 17 00:00:00 2001 From: Alejandra Gonzalez Date: Wed, 3 Dec 2025 23:17:39 +0100 Subject: [PATCH 29/56] Remove myself from rotation Turns out that I'm still in burnout, the two RFCs that I'm working on, the Rust-For-Linux support, some conferences, my blog, I got a new job that is also quite time consuming. I don't have the time to do reviews right now, I'm sorry to everyone on the team. I'll try to scale down as much as possible and then I'll come back to rotation. r? ghost --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 3bf62b6b3bba..6633b87f04e8 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -65,6 +65,7 @@ users_on_vacation = [ "Manishearth", "Alexendoo", "y21", + "blyxyas", ] [assign.owners] @@ -77,7 +78,6 @@ users_on_vacation = [ "@Alexendoo", "@dswij", "@Jarcho", - "@blyxyas", "@y21", "@samueltardieu", ] From d21adb9158d1b3628855f49c58a3b65fc24c4332 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 4 Dec 2025 20:21:27 +0100 Subject: [PATCH 30/56] bootstrap: add `rustc-dev` install target --- src/bootstrap/src/core/build_steps/dist.rs | 4 ++-- src/bootstrap/src/core/build_steps/install.rs | 11 +++++++++++ src/bootstrap/src/core/builder/mod.rs | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 40149ee09427..caf0af35e401 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -892,8 +892,8 @@ impl Step for Std { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcDev { /// The compiler that will build rustc which will be shipped in this component. - build_compiler: Compiler, - target: TargetSelection, + pub build_compiler: Compiler, + pub target: TargetSelection, } impl RustcDev { diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index d52cc52abbd3..d23fe029bcc7 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -279,6 +279,17 @@ install!((self, builder, _config), }); install_sh(builder, "rustc", self.build_compiler, Some(self.target), &tarball); }; + RustcDev, alias = "rustc-dev", Self::should_build(_config), IS_HOST: true, { + if let Some(tarball) = builder.ensure(dist::RustcDev { + build_compiler: self.build_compiler, target: self.target + }) { + install_sh(builder, "rustc-dev", self.build_compiler, Some(self.target), &tarball); + } else { + builder.info( + &format!("skipping Install RustcDev stage{} ({})", self.build_compiler.stage + 1, self.target), + ); + } + }; RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::CraneliftCodegenBackend { compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target), diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 961d0cd855ae..0bf70883a47a 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -998,6 +998,7 @@ impl<'a> Builder<'a> { // binary path, we must install rustc before the tools. Otherwise, the rust-installer will // install the same binaries twice for each tool, leaving backup files (*.old) as a result. install::Rustc, + install::RustcDev, install::Cargo, install::RustAnalyzer, install::Rustfmt, From a27bd22519f4b2acf3b2e05091250f0ce7b3f569 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Dec 2025 12:45:58 +0100 Subject: [PATCH 31/56] clean-up This mostly qualifies variable names with either `is_empty_` or `len_`, to make it easier to understand which of the two methods they relate to --- clippy_lints/src/len_zero.rs | 173 +++++++++++++++++------------------ 1 file changed, 84 insertions(+), 89 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 877bd34a732b..7c1d62951eaf 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -156,8 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() && let Some(local_id) = ty_id.as_local() && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id) - && let Some(output) = - parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) + && let Some(output) = LenOutput::new(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) { let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { Node::ForeignItem(x) => (x.ident.name, "extern type"), @@ -401,13 +400,6 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Iden } } -#[derive(Debug, Clone, Copy)] -enum LenOutput { - Integral, - Option(DefId), - Result(DefId), -} - fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { if let ty::Alias(_, alias_ty) = ty.kind() && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id) @@ -439,42 +431,49 @@ fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool { } } -fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { - if let Some(segment) = extract_future_output(cx, sig.output()) { - let res = segment.res; - - if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) { - return Some(LenOutput::Integral); - } - - if let Res::Def(_, def_id) = res - && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::Option) => Some(LenOutput::Option(def_id)), - Some(sym::Result) => Some(LenOutput::Result(def_id)), - _ => None, - } - && is_first_generic_integral(segment) - { - return Some(res); - } - - return None; - } - - match *sig.output().kind() { - ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), - ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())), - Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())), - _ => None, - }, - _ => None, - } +#[derive(Debug, Clone, Copy)] +enum LenOutput { + Integral, + Option(DefId), + Result(DefId), } impl LenOutput { - fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if let Some(segment) = extract_future_output(cx, ty) { + fn new<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { + if let Some(segment) = extract_future_output(cx, sig.output()) { + let res = segment.res; + + if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) { + return Some(Self::Integral); + } + + if let Res::Def(_, def_id) = res + && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::Option) => Some(Self::Option(def_id)), + Some(sym::Result) => Some(Self::Result(def_id)), + _ => None, + } + && is_first_generic_integral(segment) + { + return Some(res); + } + + return None; + } + + match *sig.output().kind() { + ty::Int(_) | ty::Uint(_) => Some(Self::Integral), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| Self::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| Self::Result(adt.did())), + _ => None, + }, + _ => None, + } + } + + fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, is_empty_output: Ty<'tcx>) -> bool { + if let Some(segment) = extract_future_output(cx, is_empty_output) { return match (self, segment.res) { (_, Res::PrimTy(PrimTy::Bool)) => true, (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true, @@ -483,48 +482,51 @@ impl LenOutput { }; } - match (self, ty.kind()) { + match (self, is_empty_output.kind()) { (_, &ty::Bool) => true, (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), _ => false, } } +} - fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { - let self_ref = match self_kind { - ImplicitSelfKind::RefImm => "&", - ImplicitSelfKind::RefMut => "&mut ", - _ => "", - }; - match self { - Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"), - Self::Option(_) => { - format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") - }, - Self::Result(..) => { - format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") - }, - } +/// The expected signature of `is_empty`, based on that of `len` +fn expected_is_empty_sig(len_output: LenOutput, len_self_kind: ImplicitSelfKind) -> String { + let self_ref = match len_self_kind { + ImplicitSelfKind::RefImm => "&", + ImplicitSelfKind::RefMut => "&mut ", + _ => "", + }; + match len_output { + LenOutput::Integral => format!("expected signature: `({self_ref}self) -> bool`"), + LenOutput::Option(_) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") + }, + LenOutput::Result(..) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") + }, } } /// Checks if the given signature matches the expectations for `is_empty` fn check_is_empty_sig<'tcx>( cx: &LateContext<'tcx>, - sig: FnSig<'tcx>, - self_kind: ImplicitSelfKind, + is_empty_sig: FnSig<'tcx>, + len_self_kind: ImplicitSelfKind, len_output: LenOutput, ) -> bool { - match &**sig.inputs_and_output { - [arg, res] if len_output.matches_is_empty_output(cx, *res) => { - matches!( - (arg.kind(), self_kind), - (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) - | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut) - ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut)) - }, - _ => false, + if let [is_empty_self_arg, is_empty_output] = &**is_empty_sig.inputs_and_output + && len_output.matches_is_empty_output(cx, *is_empty_output) + { + match (is_empty_self_arg.kind(), len_self_kind) { + (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) + | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut) => true, + (_, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut) if !is_empty_self_arg.is_ref() => true, + _ => false, + } + } else { + false } } @@ -532,9 +534,9 @@ fn check_is_empty_sig<'tcx>( #[expect(clippy::too_many_arguments)] fn check_for_is_empty( cx: &LateContext<'_>, - span: Span, - self_kind: ImplicitSelfKind, - output: LenOutput, + len_span: Span, + len_self_kind: ImplicitSelfKind, + len_output: LenOutput, impl_ty: DefId, item_name: Symbol, item_kind: &str, @@ -556,20 +558,14 @@ fn check_for_is_empty( .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) .find(|item| item.is_fn()); - let (msg, is_empty_span, self_kind) = match is_empty { + let (msg, is_empty_span, is_empty_expected_sig) = match is_empty { None => ( - format!( - "{item_kind} `{}` has a public `len` method, but no `is_empty` method", - item_name.as_str(), - ), + format!("{item_kind} `{item_name}` has a public `len` method, but no `is_empty` method"), None, None, ), Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => ( - format!( - "{item_kind} `{}` has a public `len` method, but a private `is_empty` method", - item_name.as_str(), - ), + format!("{item_kind} `{item_name}` has a public `len` method, but a private `is_empty` method"), Some(cx.tcx.def_span(is_empty.def_id)), None, ), @@ -578,29 +574,28 @@ fn check_for_is_empty( && check_is_empty_sig( cx, cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), - self_kind, - output, + len_self_kind, + len_output, )) => { ( format!( - "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", - item_name.as_str(), + "{item_kind} `{item_name}` has a public `len` method, but the `is_empty` method has an unexpected signature", ), Some(cx.tcx.def_span(is_empty.def_id)), - Some(self_kind), + Some(expected_is_empty_sig(len_output, len_self_kind)), ) }, Some(_) => return, }; if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) { - span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, len_span, msg, |db| { if let Some(span) = is_empty_span { db.span_note(span, "`is_empty` defined here"); } - if let Some(self_kind) = self_kind { - db.note(output.expected_sig(self_kind)); + if let Some(expected_sig) = is_empty_expected_sig { + db.note(expected_sig); } }); } From d6b561376e932fc136f2443cc733d830ec160b6d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Dec 2025 13:53:02 +0100 Subject: [PATCH 32/56] fix(len_without_is_empty): allow `is_empty(&self)` with `len(&mut self)` --- clippy_lints/src/len_zero.rs | 8 ++++++-- tests/ui/len_without_is_empty.rs | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 7c1d62951eaf..f5a832f3adfd 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -495,7 +495,7 @@ impl LenOutput { fn expected_is_empty_sig(len_output: LenOutput, len_self_kind: ImplicitSelfKind) -> String { let self_ref = match len_self_kind { ImplicitSelfKind::RefImm => "&", - ImplicitSelfKind::RefMut => "&mut ", + ImplicitSelfKind::RefMut => "&(mut) ", _ => "", }; match len_output { @@ -520,8 +520,12 @@ fn check_is_empty_sig<'tcx>( && len_output.matches_is_empty_output(cx, *is_empty_output) { match (is_empty_self_arg.kind(), len_self_kind) { + // if `len` takes `&self`, `is_empty` should do so as well (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) - | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut) => true, + // if `len` takes `&mut self`, `is_empty` may take that _or_ `&self` (#16190) + | (ty::Ref(_, _, Mutability::Mut | Mutability::Not), ImplicitSelfKind::RefMut) => true, + // if len takes `self`, `is_empty` should do so as well + // XXX: we might want to relax this to allow `&self` and `&mut self` (_, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut) if !is_empty_self_arg.is_ref() => true, _ => false, } diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 011833072d76..509348628dd6 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -473,4 +473,16 @@ impl Alias2 { } } +// Issue #16190 +pub struct RefMutLenButRefIsEmpty; +impl RefMutLenButRefIsEmpty { + pub fn len(&mut self) -> usize { + todo!() + } + + pub fn is_empty(&self) -> bool { + todo!() + } +} + fn main() {} From 2081b7a66317272a511294ee18300b5e7f1bab46 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Dec 2025 14:39:30 +0100 Subject: [PATCH 33/56] chore(len_without_is_empty): extract to a separate module It didn't share any logic at all with the lints in `len_zero.rs`, whereas the helper functions were partially mixed together, so that the file was hard to navigate --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/len_without_is_empty.rs | 342 ++++++++++++++++++++++ clippy_lints/src/len_zero.rs | 344 +---------------------- clippy_lints/src/lib.rs | 2 + 4 files changed, 352 insertions(+), 338 deletions(-) create mode 100644 clippy_lints/src/len_without_is_empty.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 17bcc3824a7b..1a02c2166454 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -245,8 +245,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO, crate::large_stack_frames::LARGE_STACK_FRAMES_INFO, crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO, + crate::len_without_is_empty::LEN_WITHOUT_IS_EMPTY_INFO, crate::len_zero::COMPARISON_TO_EMPTY_INFO, - crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO, crate::len_zero::LEN_ZERO_INFO, crate::let_if_seq::USELESS_LET_IF_SEQ_INFO, crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO, diff --git a/clippy_lints/src/len_without_is_empty.rs b/clippy_lints/src/len_without_is_empty.rs new file mode 100644 index 000000000000..1d219d7c3b74 --- /dev/null +++ b/clippy_lints/src/len_without_is_empty.rs @@ -0,0 +1,342 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::res::MaybeDef; +use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, sym}; +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_hir::{ + FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, + Node, OpaqueTyOrigin, PathSegment, PrimTy, QPath, TraitItemId, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, FnSig, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::kw; +use rustc_span::{Ident, Span, Symbol}; +use rustc_trait_selection::traits::supertrait_def_ids; + +declare_clippy_lint! { + /// ### What it does + /// Checks for items that implement `.len()` but not + /// `.is_empty()`. + /// + /// ### Why is this bad? + /// It is good custom to have both methods, because for + /// some data structures, asking about the length will be a costly operation, + /// whereas `.is_empty()` can usually answer in constant time. Also it used to + /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that + /// lint will ignore such entities. + /// + /// ### Example + /// ```ignore + /// impl X { + /// pub fn len(&self) -> usize { + /// .. + /// } + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LEN_WITHOUT_IS_EMPTY, + style, + "traits or impls with a public `len` method but no corresponding `is_empty` method" +} + +declare_lint_pass!(LenWithoutIsEmpty => [LEN_WITHOUT_IS_EMPTY]); + +impl<'tcx> LateLintPass<'tcx> for LenWithoutIsEmpty { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Trait(_, _, _, ident, _, _, trait_items) = item.kind + && !item.span.from_expansion() + { + check_trait_items(cx, item, ident, trait_items); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if item.ident.name == sym::len + && let ImplItemKind::Fn(sig, _) = &item.kind + && sig.decl.implicit_self.has_implicit_self() + && sig.decl.inputs.len() == 1 + && cx.effective_visibilities.is_exported(item.owner_id.def_id) + && matches!(sig.decl.output, FnRetTy::Return(_)) + && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) + && imp.of_trait.is_none() + && let TyKind::Path(ty_path) = &imp.self_ty.kind + && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() + && let Some(local_id) = ty_id.as_local() + && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id) + && let Some(output) = LenOutput::new(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) + { + let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { + Node::ForeignItem(x) => (x.ident.name, "extern type"), + Node::Item(x) => match x.kind { + ItemKind::Struct(ident, ..) => (ident.name, "struct"), + ItemKind::Enum(ident, ..) => (ident.name, "enum"), + ItemKind::Union(ident, ..) => (ident.name, "union"), + _ => (x.kind.ident().unwrap().name, "type"), + }, + _ => return, + }; + check_for_is_empty( + cx, + sig.span, + sig.decl.implicit_self, + output, + ty_id, + name, + kind, + item.hir_id(), + ty_hir_id, + ); + } + } +} + +fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemId]) { + fn is_named_self(cx: &LateContext<'_>, item: TraitItemId, name: Symbol) -> bool { + cx.tcx.item_name(item.owner_id) == name + && matches!( + cx.tcx.fn_arg_idents(item.owner_id), + [Some(Ident { + name: kw::SelfLower, + .. + })], + ) + } + + // fill the set with current and super traits + fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { + if set.insert(traitt) { + for supertrait in supertrait_def_ids(cx.tcx, traitt) { + fill_trait_set(supertrait, set, cx); + } + } + } + + if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id) + && trait_items.iter().any(|&i| is_named_self(cx, i, sym::len)) + { + let mut current_and_super_traits = DefIdSet::default(); + fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); + let is_empty_method_found = current_and_super_traits + .items() + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty)) + .any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1); + + if !is_empty_method_found { + span_lint( + cx, + LEN_WITHOUT_IS_EMPTY, + visited_trait.span, + format!( + "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method", + ident.name + ), + ); + } + } +} + +fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { + if let ty::Alias(_, alias_ty) = ty.kind() + && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id) + && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin + && let [GenericBound::Trait(trait_ref)] = &opaque.bounds + && let Some(segment) = trait_ref.trait_ref.path.segments.last() + && let Some(generic_args) = segment.args + && let [constraint] = generic_args.constraints + && let Some(ty) = constraint.ty() + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let [segment] = path.segments + { + return Some(segment); + } + + None +} + +fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool { + if let Some(generic_args) = segment.args + && let [GenericArg::Type(ty), ..] = &generic_args.args + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let [segment, ..] = &path.segments + && matches!(segment.res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) + { + true + } else { + false + } +} + +#[derive(Debug, Clone, Copy)] +enum LenOutput { + Integral, + Option(DefId), + Result(DefId), +} + +impl LenOutput { + fn new<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { + if let Some(segment) = extract_future_output(cx, sig.output()) { + let res = segment.res; + + if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) { + return Some(Self::Integral); + } + + if let Res::Def(_, def_id) = res + && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::Option) => Some(Self::Option(def_id)), + Some(sym::Result) => Some(Self::Result(def_id)), + _ => None, + } + && is_first_generic_integral(segment) + { + return Some(res); + } + + return None; + } + + match *sig.output().kind() { + ty::Int(_) | ty::Uint(_) => Some(Self::Integral), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| Self::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| Self::Result(adt.did())), + _ => None, + }, + _ => None, + } + } + + fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, is_empty_output: Ty<'tcx>) -> bool { + if let Some(segment) = extract_future_output(cx, is_empty_output) { + return match (self, segment.res) { + (_, Res::PrimTy(PrimTy::Bool)) => true, + (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true, + (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true, + _ => false, + }; + } + + match (self, is_empty_output.kind()) { + (_, &ty::Bool) => true, + (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), + (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), + _ => false, + } + } +} + +/// The expected signature of `is_empty`, based on that of `len` +fn expected_is_empty_sig(len_output: LenOutput, len_self_kind: ImplicitSelfKind) -> String { + let self_ref = match len_self_kind { + ImplicitSelfKind::RefImm => "&", + ImplicitSelfKind::RefMut => "&(mut) ", + _ => "", + }; + match len_output { + LenOutput::Integral => format!("expected signature: `({self_ref}self) -> bool`"), + LenOutput::Option(_) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") + }, + LenOutput::Result(..) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") + }, + } +} + +/// Checks if the given signature matches the expectations for `is_empty` +fn check_is_empty_sig<'tcx>( + cx: &LateContext<'tcx>, + is_empty_sig: FnSig<'tcx>, + len_self_kind: ImplicitSelfKind, + len_output: LenOutput, +) -> bool { + if let [is_empty_self_arg, is_empty_output] = &**is_empty_sig.inputs_and_output + && len_output.matches_is_empty_output(cx, *is_empty_output) + { + match (is_empty_self_arg.kind(), len_self_kind) { + // if `len` takes `&self`, `is_empty` should do so as well + (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) + // if `len` takes `&mut self`, `is_empty` may take that _or_ `&self` (#16190) + | (ty::Ref(_, _, Mutability::Mut | Mutability::Not), ImplicitSelfKind::RefMut) => true, + // if len takes `self`, `is_empty` should do so as well + // XXX: we might want to relax this to allow `&self` and `&mut self` + (_, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut) if !is_empty_self_arg.is_ref() => true, + _ => false, + } + } else { + false + } +} + +/// Checks if the given type has an `is_empty` method with the appropriate signature. +#[expect(clippy::too_many_arguments)] +fn check_for_is_empty( + cx: &LateContext<'_>, + len_span: Span, + len_self_kind: ImplicitSelfKind, + len_output: LenOutput, + impl_ty: DefId, + item_name: Symbol, + item_kind: &str, + len_method_hir_id: HirId, + ty_decl_hir_id: HirId, +) { + // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to + // find the correct inherent impls. + let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() { + adt.did() + } else { + return; + }; + + let is_empty = cx + .tcx + .inherent_impls(impl_ty) + .iter() + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) + .find(|item| item.is_fn()); + + let (msg, is_empty_span, is_empty_expected_sig) = match is_empty { + None => ( + format!("{item_kind} `{item_name}` has a public `len` method, but no `is_empty` method"), + None, + None, + ), + Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => ( + format!("{item_kind} `{item_name}` has a public `len` method, but a private `is_empty` method"), + Some(cx.tcx.def_span(is_empty.def_id)), + None, + ), + Some(is_empty) + if !(is_empty.is_method() + && check_is_empty_sig( + cx, + cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), + len_self_kind, + len_output, + )) => + { + ( + format!( + "{item_kind} `{item_name}` has a public `len` method, but the `is_empty` method has an unexpected signature", + ), + Some(cx.tcx.def_span(is_empty.def_id)), + Some(expected_is_empty_sig(len_output, len_self_kind)), + ) + }, + Some(_) => return, + }; + + if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) { + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, len_span, msg, |db| { + if let Some(span) = is_empty_span { + db.span_note(span, "`is_empty` defined here"); + } + if let Some(expected_sig) = is_empty_expected_sig { + db.note(expected_sig); + } + }); + } +} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f5a832f3adfd..2e576da38b89 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,27 +1,20 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym}; +use clippy_utils::{parent_item_name, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::{ - BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, - Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, RustcVersion, - StabilityLevel, StableSince, TraitItemId, TyKind, -}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, PatExprKind, PatKind, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, FnSig, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::kw; -use rustc_span::{Ident, Span, Symbol}; -use rustc_trait_selection::traits::supertrait_def_ids; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -58,32 +51,6 @@ declare_clippy_lint! { "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead" } -declare_clippy_lint! { - /// ### What it does - /// Checks for items that implement `.len()` but not - /// `.is_empty()`. - /// - /// ### Why is this bad? - /// It is good custom to have both methods, because for - /// some data structures, asking about the length will be a costly operation, - /// whereas `.is_empty()` can usually answer in constant time. Also it used to - /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that - /// lint will ignore such entities. - /// - /// ### Example - /// ```ignore - /// impl X { - /// pub fn len(&self) -> usize { - /// .. - /// } - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LEN_WITHOUT_IS_EMPTY, - style, - "traits or impls with a public `len` method but no corresponding `is_empty` method" -} - declare_clippy_lint! { /// ### What it does /// Checks for comparing to an empty slice such as `""` or `[]`, @@ -126,7 +93,7 @@ pub struct LenZero { msrv: Msrv, } -impl_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); +impl_lint_pass!(LenZero => [LEN_ZERO, COMPARISON_TO_EMPTY]); impl LenZero { pub fn new(conf: &'static Conf) -> Self { @@ -135,53 +102,6 @@ impl LenZero { } impl<'tcx> LateLintPass<'tcx> for LenZero { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Trait(_, _, _, ident, _, _, trait_items) = item.kind - && !item.span.from_expansion() - { - check_trait_items(cx, item, ident, trait_items); - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if item.ident.name == sym::len - && let ImplItemKind::Fn(sig, _) = &item.kind - && sig.decl.implicit_self.has_implicit_self() - && sig.decl.inputs.len() == 1 - && cx.effective_visibilities.is_exported(item.owner_id.def_id) - && matches!(sig.decl.output, FnRetTy::Return(_)) - && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) - && imp.of_trait.is_none() - && let TyKind::Path(ty_path) = &imp.self_ty.kind - && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() - && let Some(local_id) = ty_id.as_local() - && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id) - && let Some(output) = LenOutput::new(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) - { - let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { - Node::ForeignItem(x) => (x.ident.name, "extern type"), - Node::Item(x) => match x.kind { - ItemKind::Struct(ident, ..) => (ident.name, "struct"), - ItemKind::Enum(ident, ..) => (ident.name, "enum"), - ItemKind::Union(ident, ..) => (ident.name, "union"), - _ => (x.kind.ident().unwrap().name, "type"), - }, - _ => return, - }; - check_for_is_empty( - cx, - sig.span, - sig.decl.implicit_self, - output, - ty_id, - name, - kind, - item.hir_id(), - ty_hir_id, - ); - } - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { @@ -355,256 +275,6 @@ fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { } } -fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemId]) { - fn is_named_self(cx: &LateContext<'_>, item: TraitItemId, name: Symbol) -> bool { - cx.tcx.item_name(item.owner_id) == name - && matches!( - cx.tcx.fn_arg_idents(item.owner_id), - [Some(Ident { - name: kw::SelfLower, - .. - })], - ) - } - - // fill the set with current and super traits - fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { - if set.insert(traitt) { - for supertrait in supertrait_def_ids(cx.tcx, traitt) { - fill_trait_set(supertrait, set, cx); - } - } - } - - if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id) - && trait_items.iter().any(|&i| is_named_self(cx, i, sym::len)) - { - let mut current_and_super_traits = DefIdSet::default(); - fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); - let is_empty_method_found = current_and_super_traits - .items() - .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty)) - .any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1); - - if !is_empty_method_found { - span_lint( - cx, - LEN_WITHOUT_IS_EMPTY, - visited_trait.span, - format!( - "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method", - ident.name - ), - ); - } - } -} - -fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { - if let ty::Alias(_, alias_ty) = ty.kind() - && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id) - && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin - && let [GenericBound::Trait(trait_ref)] = &opaque.bounds - && let Some(segment) = trait_ref.trait_ref.path.segments.last() - && let Some(generic_args) = segment.args - && let [constraint] = generic_args.constraints - && let Some(ty) = constraint.ty() - && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind - && let [segment] = path.segments - { - return Some(segment); - } - - None -} - -fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool { - if let Some(generic_args) = segment.args - && let [GenericArg::Type(ty), ..] = &generic_args.args - && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind - && let [segment, ..] = &path.segments - && matches!(segment.res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) - { - true - } else { - false - } -} - -#[derive(Debug, Clone, Copy)] -enum LenOutput { - Integral, - Option(DefId), - Result(DefId), -} - -impl LenOutput { - fn new<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { - if let Some(segment) = extract_future_output(cx, sig.output()) { - let res = segment.res; - - if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) { - return Some(Self::Integral); - } - - if let Res::Def(_, def_id) = res - && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::Option) => Some(Self::Option(def_id)), - Some(sym::Result) => Some(Self::Result(def_id)), - _ => None, - } - && is_first_generic_integral(segment) - { - return Some(res); - } - - return None; - } - - match *sig.output().kind() { - ty::Int(_) | ty::Uint(_) => Some(Self::Integral), - ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => subs.type_at(0).is_integral().then(|| Self::Option(adt.did())), - Some(sym::Result) => subs.type_at(0).is_integral().then(|| Self::Result(adt.did())), - _ => None, - }, - _ => None, - } - } - - fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, is_empty_output: Ty<'tcx>) -> bool { - if let Some(segment) = extract_future_output(cx, is_empty_output) { - return match (self, segment.res) { - (_, Res::PrimTy(PrimTy::Bool)) => true, - (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true, - (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true, - _ => false, - }; - } - - match (self, is_empty_output.kind()) { - (_, &ty::Bool) => true, - (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), - (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), - _ => false, - } - } -} - -/// The expected signature of `is_empty`, based on that of `len` -fn expected_is_empty_sig(len_output: LenOutput, len_self_kind: ImplicitSelfKind) -> String { - let self_ref = match len_self_kind { - ImplicitSelfKind::RefImm => "&", - ImplicitSelfKind::RefMut => "&(mut) ", - _ => "", - }; - match len_output { - LenOutput::Integral => format!("expected signature: `({self_ref}self) -> bool`"), - LenOutput::Option(_) => { - format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") - }, - LenOutput::Result(..) => { - format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") - }, - } -} - -/// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig<'tcx>( - cx: &LateContext<'tcx>, - is_empty_sig: FnSig<'tcx>, - len_self_kind: ImplicitSelfKind, - len_output: LenOutput, -) -> bool { - if let [is_empty_self_arg, is_empty_output] = &**is_empty_sig.inputs_and_output - && len_output.matches_is_empty_output(cx, *is_empty_output) - { - match (is_empty_self_arg.kind(), len_self_kind) { - // if `len` takes `&self`, `is_empty` should do so as well - (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) - // if `len` takes `&mut self`, `is_empty` may take that _or_ `&self` (#16190) - | (ty::Ref(_, _, Mutability::Mut | Mutability::Not), ImplicitSelfKind::RefMut) => true, - // if len takes `self`, `is_empty` should do so as well - // XXX: we might want to relax this to allow `&self` and `&mut self` - (_, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut) if !is_empty_self_arg.is_ref() => true, - _ => false, - } - } else { - false - } -} - -/// Checks if the given type has an `is_empty` method with the appropriate signature. -#[expect(clippy::too_many_arguments)] -fn check_for_is_empty( - cx: &LateContext<'_>, - len_span: Span, - len_self_kind: ImplicitSelfKind, - len_output: LenOutput, - impl_ty: DefId, - item_name: Symbol, - item_kind: &str, - len_method_hir_id: HirId, - ty_decl_hir_id: HirId, -) { - // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to - // find the correct inherent impls. - let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() { - adt.did() - } else { - return; - }; - - let is_empty = cx - .tcx - .inherent_impls(impl_ty) - .iter() - .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) - .find(|item| item.is_fn()); - - let (msg, is_empty_span, is_empty_expected_sig) = match is_empty { - None => ( - format!("{item_kind} `{item_name}` has a public `len` method, but no `is_empty` method"), - None, - None, - ), - Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => ( - format!("{item_kind} `{item_name}` has a public `len` method, but a private `is_empty` method"), - Some(cx.tcx.def_span(is_empty.def_id)), - None, - ), - Some(is_empty) - if !(is_empty.is_method() - && check_is_empty_sig( - cx, - cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), - len_self_kind, - len_output, - )) => - { - ( - format!( - "{item_kind} `{item_name}` has a public `len` method, but the `is_empty` method has an unexpected signature", - ), - Some(cx.tcx.def_span(is_empty.def_id)), - Some(expected_is_empty_sig(len_output, len_self_kind)), - ) - }, - Some(_) => return, - }; - - if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) { - span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, len_span, msg, |db| { - if let Some(span) = is_empty_span { - db.span_note(span, "`is_empty` defined here"); - } - if let Some(expected_sig) = is_empty_expected_sig { - db.note(expected_sig); - } - }); - } -} - fn is_empty_string(expr: &Expr<'_>) -> bool { if let ExprKind::Lit(lit) = expr.kind && let LitKind::Str(lit, _) = lit.node diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cad36b7f197a..5b39d8844797 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -182,6 +182,7 @@ mod large_include_file; mod large_stack_arrays; mod large_stack_frames; mod legacy_numeric_constants; +mod len_without_is_empty; mod len_zero; mod let_if_seq; mod let_underscore; @@ -539,6 +540,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)), Box::new(|_| Box::>::default()), Box::new(move |_| Box::new(len_zero::LenZero::new(conf))), + Box::new(|_| Box::new(len_without_is_empty::LenWithoutIsEmpty)), Box::new(move |_| Box::new(attrs::Attributes::new(conf))), Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)), Box::new(|_| Box::new(unicode::Unicode)), From c9055034744c98739e087a2a7f3197f4e547aed2 Mon Sep 17 00:00:00 2001 From: Linshu Yang Date: Fri, 5 Dec 2025 18:18:58 +0000 Subject: [PATCH 34/56] fix: `panicking_unwrap` FP on field access with implicit deref --- clippy_lints/src/unwrap.rs | 9 +++++++-- .../ui/checked_unwrap/simple_conditionals.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 8ed3df8731b3..a95f2b681add 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -180,14 +180,19 @@ impl Local { field_indices, .. } => { + let field_projections = place + .projections + .iter() + .filter(|proj| matches!(proj.kind, ProjectionKind::Field(_, _))) + .collect::>(); is_potentially_local_place(*local_id, place) // If there were projections other than field projections, err on the side of caution and say that they // _might_ be mutating something. // // The reason we use `<=` and not `==` is that a mutation of `struct` or `struct.field1` should count as // mutation of the child fields such as `struct.field1.field2` - && place.projections.len() <= field_indices.len() - && iter::zip(&place.projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| { + && field_projections.len() <= field_indices.len() + && iter::zip(&field_projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| { match proj.kind { ProjectionKind::Field(f_idx, _) => f_idx == field_idx, // If this is a projection we don't expect, it _might_ be mutating something diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index c6476a7507a1..bab20b091d38 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -472,3 +472,22 @@ fn issue15321() { //~^ unnecessary_unwrap } } + +mod issue16188 { + struct Foo { + value: Option, + } + + impl Foo { + pub fn bar(&mut self) { + let print_value = |v: i32| { + println!("{}", v); + }; + + if self.value.is_none() { + self.value = Some(10); + print_value(self.value.unwrap()); + } + } + } +} From c788c7cb77509b8704da3174fe7c7ecad041f3ad Mon Sep 17 00:00:00 2001 From: irelaxcn Date: Sun, 7 Dec 2025 00:41:43 +0800 Subject: [PATCH 35/56] Fix `map_entry` FP when it would cause `MutexGuard` to be held across an await point. --- clippy_lints/src/entry.rs | 48 ++++++++++++++++++++++++++++++--------- tests/ui/entry.fixed | 24 ++++++++++++++++++++ tests/ui/entry.rs | 24 ++++++++++++++++++++ 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a5ec6777b434..bdfe2e49e66e 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -3,11 +3,12 @@ use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_appl use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, - peel_hir_expr_while, + SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr, + is_expr_used_or_unified, paths, peel_hir_expr_while, }; use core::fmt::{self, Write}; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{Visitor, walk_body, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp}; @@ -382,6 +383,8 @@ struct InsertSearcher<'cx, 'tcx> { loops: Vec, /// Local variables created in the expression. These don't need to be captured. locals: HirIdSet, + /// Whether the map is a non-async-aware `MutexGuard`. + map_is_mutex_guard: bool, } impl<'tcx> InsertSearcher<'_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but @@ -524,15 +527,22 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; self.visit_non_tail_expr(cond_expr); - // Each branch may contain it's own insert expression. + // Each branch may contain its own insert expression. let mut is_map_used = self.visit_cond_arm(then_expr); is_map_used |= self.visit_cond_arm(else_expr); self.is_map_used = is_map_used; }, ExprKind::Match(scrutinee_expr, arms, _) => { + // If the map is a non-async-aware `MutexGuard` and + // `.await` expression appears alongside map insertion in the same `then` or `else` block, + // we cannot suggest using `entry()` because it would hold the lock across the await point, + // triggering `await_holding_lock` and risking deadlock. + if self.map_is_mutex_guard && desugar_await(expr).is_some() { + self.can_use_entry = false; + } self.is_single_insert = false; self.visit_non_tail_expr(scrutinee_expr); - // Each branch may contain it's own insert expression. + // Each branch may contain its own insert expression. let mut is_map_used = self.is_map_used; for arm in arms { self.visit_pat(arm.pat); @@ -725,16 +735,32 @@ fn find_insert_calls<'tcx>( edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), + map_is_mutex_guard: false, }; + // Check if the map is a non-async-aware `MutexGuard` + if let rustc_middle::ty::Adt(adt, _) = cx.typeck_results().expr_ty(contains_expr.map).kind() + && is_mutex_guard(cx, adt.did()) + { + s.map_is_mutex_guard = true; + } + s.visit_expr(expr); - let allow_insert_closure = s.allow_insert_closure; - let is_single_insert = s.is_single_insert; + if !s.can_use_entry { + return None; + } + let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key)); - let edits = s.edits; - s.can_use_entry.then_some(InsertSearchResults { - edits, - allow_insert_closure, - is_single_insert, + Some(InsertSearchResults { + edits: s.edits, + allow_insert_closure: s.allow_insert_closure, + is_single_insert: s.is_single_insert, is_key_used_and_no_copy, }) } + +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { + match cx.tcx.get_diagnostic_name(def_id) { + Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), + None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), + } +} diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index f2df9f0204ea..1e36ca4f1f09 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -248,4 +248,28 @@ mod issue14449 { } } +// Don't suggest when it would cause `MutexGuard` to be held across an await point. +mod issue_16173 { + use std::collections::HashMap; + use std::sync::Mutex; + + async fn f() {} + + async fn foo() { + let mu_map = Mutex::new(HashMap::new()); + if !mu_map.lock().unwrap().contains_key(&0) { + f().await; + mu_map.lock().unwrap().insert(0, 0); + } + + if mu_map.lock().unwrap().contains_key(&1) { + todo!(); + } else { + mu_map.lock().unwrap().insert(1, 42); + todo!(); + f().await; + } + } +} + fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index 166eea417ac2..b3da0ef3ffd6 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -254,4 +254,28 @@ mod issue14449 { } } +// Don't suggest when it would cause `MutexGuard` to be held across an await point. +mod issue_16173 { + use std::collections::HashMap; + use std::sync::Mutex; + + async fn f() {} + + async fn foo() { + let mu_map = Mutex::new(HashMap::new()); + if !mu_map.lock().unwrap().contains_key(&0) { + f().await; + mu_map.lock().unwrap().insert(0, 0); + } + + if mu_map.lock().unwrap().contains_key(&1) { + todo!(); + } else { + mu_map.lock().unwrap().insert(1, 42); + todo!(); + f().await; + } + } +} + fn main() {} From 8cc9c5ebb19a8d594ac36e2e33307f5def7b60fc Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 28 Sep 2025 18:12:17 -0400 Subject: [PATCH 36/56] Add warn-by-default lint for visibility on `const _` declarations Add a warn-by-default `unused_visibility` lint for visibility qualifiers on `const _` declarations - e.g. `pub const _: () = ();`. These have no effect. --- tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr | 6 +++--- tests/ui-toml/absolute_paths/absolute_paths.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr index 70d71f6c4ea1..c94b7777f3e9 100644 --- a/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr +++ b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr @@ -71,10 +71,10 @@ LL | impl core::fmt::Display for X | ^^^^^^^^^^^^^^^^^^ error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:113:14 + --> tests/ui-toml/absolute_paths/absolute_paths.rs:113:10 | -LL | pub const _: crate::S = { - | ^^^^^^^^ +LL | const _: crate::S = { + | ^^^^^^^^ error: consider bringing this path into scope with the `use` keyword --> tests/ui-toml/absolute_paths/absolute_paths.rs:114:9 diff --git a/tests/ui-toml/absolute_paths/absolute_paths.rs b/tests/ui-toml/absolute_paths/absolute_paths.rs index c024f2f513ce..a3982b8f6540 100644 --- a/tests/ui-toml/absolute_paths/absolute_paths.rs +++ b/tests/ui-toml/absolute_paths/absolute_paths.rs @@ -110,7 +110,7 @@ mod m1 { } //~[no_short]v absolute_paths -pub const _: crate::S = { +const _: crate::S = { let crate::S = m1::S; //~[no_short] absolute_paths crate::m1::S From 4648af67516fe0387f287486b5f19f7518032423 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 8 Dec 2025 18:42:10 +0100 Subject: [PATCH 37/56] Remove `[no-mentions]` handler in our triagebot config https://github.blog/changelog/2025-11-07-removing-notifications-for-mentions-in-commit-messages/ --- triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 6633b87f04e8..09dec7675e7e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -22,9 +22,6 @@ allow-unauthenticated = [ [mentions."clippy_lints/src/doc"] cc = ["@notriddle"] -# Prevents mentions in commits to avoid users being spammed -[no-mentions] - # Have rustbot inform users about the *No Merge Policy* [no-merges] exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust From 3551aeb5c3c4570e170cd433892eaa3f2507ce2e Mon Sep 17 00:00:00 2001 From: Linshu Yang Date: Fri, 5 Dec 2025 21:02:51 +0000 Subject: [PATCH 38/56] fix: `tuple_array_conversions` FP when binded vars are used before conversion --- clippy_lints/src/tuple_array_conversions.rs | 41 +++++++++++++++------ tests/ui/tuple_array_conversions.rs | 23 ++++++++++++ tests/ui/tuple_array_conversions.stderr | 10 ++++- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 5d0945bece55..98bf9f9fea58 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::visitors::local_used_once; +use clippy_utils::{get_enclosing_block, is_from_proc_macro}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, Node, PatKind}; @@ -11,7 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use std::iter::once; -use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -86,7 +85,7 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & ExprKind::Path(_) => Some(elements.iter().collect()), _ => None, }) - && all_bindings_are_for_conv(cx, &[ty], expr, elements, &locals, ToType::Array) + && all_bindings_are_for_conv(cx, &[ty], elements, &locals, ToType::Array) && !is_from_proc_macro(cx, expr) { span_lint_and_help( @@ -123,7 +122,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & ExprKind::Path(_) => Some(elements.iter().collect()), _ => None, }) - && all_bindings_are_for_conv(cx, tys, expr, elements, &locals, ToType::Tuple) + && all_bindings_are_for_conv(cx, tys, elements, &locals, ToType::Tuple) && !is_from_proc_macro(cx, expr) { span_lint_and_help( @@ -148,7 +147,6 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & fn all_bindings_are_for_conv<'tcx>( cx: &LateContext<'tcx>, final_tys: &[Ty<'tcx>], - expr: &Expr<'_>, elements: &[Expr<'_>], locals: &[&Expr<'_>], kind: ToType, @@ -166,13 +164,30 @@ fn all_bindings_are_for_conv<'tcx>( _ => None, }) .all_equal() - // Fix #11124, very convenient utils function! ❤️ - && locals - .iter() - .all(|&l| for_each_local_use_after_expr(cx, l, expr.hir_id, |_| ControlFlow::Break::<()>(())).is_continue()) + && locals.iter().zip(local_parents.iter()).all(|(&l, &parent)| { + if let Node::LetStmt(_) = parent { + return true; + } + + let Some(b) = get_enclosing_block(cx, l) else { + return true; + }; + local_used_once(cx, b, l).is_some() + }) && local_parents.first().is_some_and(|node| { let Some(ty) = match node { - Node::Pat(pat) => Some(pat.hir_id), + Node::Pat(pat) + if let PatKind::Tuple(pats, _) | PatKind::Slice(pats, None, []) = &pat.kind + && pats.iter().zip(locals.iter()).all(|(p, l)| { + if let PatKind::Binding(_, id, _, _) = p.kind { + id == *l + } else { + true + } + }) => + { + Some(pat.hir_id) + }, Node::LetStmt(l) => Some(l.hir_id), _ => None, } @@ -186,7 +201,9 @@ fn all_bindings_are_for_conv<'tcx>( tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal() }, (ToType::Tuple, ty::Array(ty, len)) => { - let Some(len) = len.try_to_target_usize(cx.tcx) else { return false }; + let Some(len) = len.try_to_target_usize(cx.tcx) else { + return false; + }; len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal() }, _ => false, diff --git a/tests/ui/tuple_array_conversions.rs b/tests/ui/tuple_array_conversions.rs index 772c41df090e..17e6a252b266 100644 --- a/tests/ui/tuple_array_conversions.rs +++ b/tests/ui/tuple_array_conversions.rs @@ -116,3 +116,26 @@ fn msrv_juust_right() { let x = &[1, 2]; let x = (x[0], x[1]); } + +fn issue16192() { + fn do_something(tuple: (u32, u32)) {} + fn produce_array() -> [u32; 2] { + [1, 2] + } + + let [a, b] = produce_array(); + for tuple in [(a, b), (b, a)] { + do_something(tuple); + } + + let [a, b] = produce_array(); + let x = b; + do_something((a, b)); + + let [a, b] = produce_array(); + do_something((b, a)); + + let [a, b] = produce_array(); + do_something((a, b)); + //~^ tuple_array_conversions +} diff --git a/tests/ui/tuple_array_conversions.stderr b/tests/ui/tuple_array_conversions.stderr index 6dafb8d285d4..4c15769b7487 100644 --- a/tests/ui/tuple_array_conversions.stderr +++ b/tests/ui/tuple_array_conversions.stderr @@ -80,5 +80,13 @@ LL | let x = [x.0, x.1]; | = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed -error: aborting due to 10 previous errors +error: it looks like you're trying to convert an array to a tuple + --> tests/ui/tuple_array_conversions.rs:139:18 + | +LL | do_something((a, b)); + | ^^^^^^ + | + = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed + +error: aborting due to 11 previous errors From 94aa13a09c940202da24ad0a1ffcd986e4df2df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 4 Nov 2025 18:41:04 +0000 Subject: [PATCH 39/56] Recognize `type Alias = dyn Trait` in `fn` return types ``` error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time --> $DIR/dyn-trait-type-alias-return-type.rs:4:11 | LL | fn f() -> T { loop {} } | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` note: this type alias is unsized --> $DIR/dyn-trait-type-alias-return-type.rs:1:1 | LL | type T = dyn core::fmt::Debug; | ^^^^^^ = note: the return type of a function must have a statically known size ``` --- tests/ui/future_not_send.stderr | 37 +++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index e366dc2d2195..8b8af1ebaed3 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -1,8 +1,8 @@ error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:8:1 + --> tests/ui/future_not_send.rs:8:62 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send` + | ^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await --> tests/ui/future_not_send.rs:11:20 @@ -23,10 +23,10 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:14:1 + --> tests/ui/future_not_send.rs:14:41 | LL | pub async fn public_future(rc: Rc<[u8]>) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` + | ^ future returned by `public_future` is not `Send` | note: future is not `Send` as this value is used across an await --> tests/ui/future_not_send.rs:17:20 @@ -39,10 +39,10 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:24:1 + --> tests/ui/future_not_send.rs:24:63 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future2` is not `Send` + | ^^^^ future returned by `private_future2` is not `Send` | note: captured value is not `Send` --> tests/ui/future_not_send.rs:24:26 @@ -58,10 +58,10 @@ LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { = note: `std::cell::Cell` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:30:1 + --> tests/ui/future_not_send.rs:30:42 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future2` is not `Send` + | ^ future returned by `public_future2` is not `Send` | note: captured value is not `Send` --> tests/ui/future_not_send.rs:30:29 @@ -71,10 +71,10 @@ LL | pub async fn public_future2(rc: Rc<[u8]>) {} = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:42:5 + --> tests/ui/future_not_send.rs:42:39 | LL | async fn private_future(&self) -> usize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send` + | ^^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await --> tests/ui/future_not_send.rs:45:24 @@ -87,10 +87,10 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:49:5 + --> tests/ui/future_not_send.rs:49:38 | LL | pub async fn public_future(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` + | ^ future returned by `public_future` is not `Send` | note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` --> tests/ui/future_not_send.rs:49:32 @@ -100,13 +100,10 @@ LL | pub async fn public_future(&self) { = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:61:1 + --> tests/ui/future_not_send.rs:61:37 | -LL | / async fn generic_future(t: T) -> T -LL | | -LL | | where -LL | | T: Send, - | |____________^ future returned by `generic_future` is not `Send` +LL | async fn generic_future(t: T) -> T + | ^ future returned by `generic_future` is not `Send` | note: future is not `Send` as this value is used across an await --> tests/ui/future_not_send.rs:67:20 @@ -118,10 +115,10 @@ LL | async { true }.await; = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:83:1 + --> tests/ui/future_not_send.rs:83:51 | LL | async fn generic_future_always_unsend(_: Rc) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `generic_future_always_unsend` is not `Send` + | ^ future returned by `generic_future_always_unsend` is not `Send` | note: future is not `Send` as this value is used across an await --> tests/ui/future_not_send.rs:86:20 From d0e4b5b159d2df4ba7c6c113171e3f6049d889f9 Mon Sep 17 00:00:00 2001 From: irelaxcn Date: Wed, 10 Dec 2025 01:34:25 +0800 Subject: [PATCH 40/56] Fix typo in deprecated lint `string_to_string` message --- clippy_lints/src/deprecated_lints.rs | 2 +- tests/ui/deprecated.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index f010d17917f9..6e62e983d2f3 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "1.91.0"] - ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), + ("clippy::string_to_string", "`clippy::implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index cd225da611c4..fa70371b926f 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -61,7 +61,7 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can n LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::string_to_string` has been removed: `clippy:implicit_clone` covers those cases +error: lint `clippy::string_to_string` has been removed: `clippy::implicit_clone` covers those cases --> tests/ui/deprecated.rs:15:9 | LL | #![warn(clippy::string_to_string)] From 1232c81a3eb6403b04a1795fe5e4bdb678f236fe Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Tue, 9 Dec 2025 22:56:23 +0100 Subject: [PATCH 41/56] Add needless_type_cast lint --- CHANGELOG.md | 1 + README.md | 2 +- book/src/README.md | 2 +- clippy_lints/src/casts/mod.rs | 32 ++ clippy_lints/src/casts/needless_type_cast.rs | 289 +++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + tests/ui/needless_type_cast.fixed | 182 ++++++++++++ tests/ui/needless_type_cast.rs | 182 ++++++++++++ tests/ui/needless_type_cast.stderr | 71 +++++ 9 files changed, 760 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/casts/needless_type_cast.rs create mode 100644 tests/ui/needless_type_cast.fixed create mode 100644 tests/ui/needless_type_cast.rs create mode 100644 tests/ui/needless_type_cast.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c81fa47eca9..dfbd07e69e53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6688,6 +6688,7 @@ Released 2018-09-13 [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark [`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn +[`needless_type_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_type_cast [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply diff --git a/README.md b/README.md index 20a5e997e629..78498c73ae78 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 800 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 5d2c3972b060..c5b264c9f703 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 800 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 47cc1da0a6e9..494d6180d3cb 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -19,6 +19,7 @@ mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod manual_dangling_ptr; +mod needless_type_cast; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -813,6 +814,32 @@ declare_clippy_lint! { "casting a primitive method pointer to any integer type" } +declare_clippy_lint! { + /// ### What it does + /// Checks for bindings (constants, statics, or let bindings) that are defined + /// with one numeric type but are consistently cast to a different type in all usages. + /// + /// ### Why is this bad? + /// If a binding is always cast to a different type when used, it would be clearer + /// and more efficient to define it with the target type from the start. + /// + /// ### Example + /// ```no_run + /// const SIZE: u16 = 15; + /// let arr: [u8; SIZE as usize] = [0; SIZE as usize]; + /// ``` + /// + /// Use instead: + /// ```no_run + /// const SIZE: usize = 15; + /// let arr: [u8; SIZE] = [0; SIZE]; + /// ``` + #[clippy::version = "1.93.0"] + pub NEEDLESS_TYPE_CAST, + pedantic, + "binding defined with one type but always cast to another" +} + pub struct Casts { msrv: Msrv, } @@ -851,6 +878,7 @@ impl_lint_pass!(Casts => [ AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, CONFUSING_METHOD_TO_NUMERIC_CAST, + NEEDLESS_TYPE_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -920,4 +948,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_slice_different_sizes::check(cx, expr, self.msrv); ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } + + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &rustc_hir::Body<'tcx>) { + needless_type_cast::check(cx, body); + } } diff --git a/clippy_lints/src/casts/needless_type_cast.rs b/clippy_lints/src/casts/needless_type_cast.rs new file mode 100644 index 000000000000..ca6aa0f87bbf --- /dev/null +++ b/clippy_lints/src/casts/needless_type_cast.rs @@ -0,0 +1,289 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::visitors::{Descend, for_each_expr, for_each_expr_without_closures}; +use core::ops::ControlFlow; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{BlockCheckMode, Body, Expr, ExprKind, HirId, LetStmt, PatKind, StmtKind, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::ty::{Ty, TypeVisitableExt}; +use rustc_span::Span; + +use super::NEEDLESS_TYPE_CAST; + +struct BindingInfo<'a> { + source_ty: Ty<'a>, + ty_span: Span, +} + +struct UsageInfo<'a> { + cast_to: Option>, + in_generic_context: bool, +} + +pub(super) fn check<'a>(cx: &LateContext<'a>, body: &Body<'a>) { + let mut bindings: FxHashMap> = FxHashMap::default(); + + for_each_expr_without_closures(body.value, |expr| { + match expr.kind { + ExprKind::Block(block, _) => { + for stmt in block.stmts { + if let StmtKind::Let(let_stmt) = stmt.kind { + collect_binding_from_local(cx, let_stmt, &mut bindings); + } + } + }, + ExprKind::Let(let_expr) => { + collect_binding_from_let(cx, let_expr, &mut bindings); + }, + _ => {}, + } + ControlFlow::<()>::Continue(()) + }); + + #[allow(rustc::potential_query_instability)] + let mut binding_vec: Vec<_> = bindings.into_iter().collect(); + binding_vec.sort_by_key(|(_, info)| info.ty_span.lo()); + + for (hir_id, binding_info) in binding_vec { + check_binding_usages(cx, body, hir_id, &binding_info); + } +} + +fn collect_binding_from_let<'a>( + cx: &LateContext<'a>, + let_expr: &rustc_hir::LetExpr<'a>, + bindings: &mut FxHashMap>, +) { + if let_expr.ty.is_none() + || let_expr.span.from_expansion() + || has_generic_return_type(cx, let_expr.init) + || contains_unsafe(let_expr.init) + { + return; + } + + if let PatKind::Binding(_, hir_id, _, _) = let_expr.pat.kind + && let Some(ty_hir) = let_expr.ty + { + let ty = cx.typeck_results().pat_ty(let_expr.pat); + if ty.is_numeric() { + bindings.insert( + hir_id, + BindingInfo { + source_ty: ty, + ty_span: ty_hir.span, + }, + ); + } + } +} + +fn collect_binding_from_local<'a>( + cx: &LateContext<'a>, + let_stmt: &LetStmt<'a>, + bindings: &mut FxHashMap>, +) { + if let_stmt.ty.is_none() + || let_stmt.span.from_expansion() + || let_stmt + .init + .is_some_and(|init| has_generic_return_type(cx, init) || contains_unsafe(init)) + { + return; + } + + if let PatKind::Binding(_, hir_id, _, _) = let_stmt.pat.kind + && let Some(ty_hir) = let_stmt.ty + { + let ty = cx.typeck_results().pat_ty(let_stmt.pat); + if ty.is_numeric() { + bindings.insert( + hir_id, + BindingInfo { + source_ty: ty, + ty_span: ty_hir.span, + }, + ); + } + } +} + +fn contains_unsafe(expr: &Expr<'_>) -> bool { + for_each_expr_without_closures(expr, |e| { + if let ExprKind::Block(block, _) = e.kind + && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules + { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + }) + .is_some() +} + +fn has_generic_return_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Block(block, _) => { + if let Some(tail_expr) = block.expr { + return has_generic_return_type(cx, tail_expr); + } + false + }, + ExprKind::If(_, then_block, else_expr) => { + has_generic_return_type(cx, then_block) || else_expr.is_some_and(|e| has_generic_return_type(cx, e)) + }, + ExprKind::Match(_, arms, _) => arms.iter().any(|arm| has_generic_return_type(cx, arm.body)), + ExprKind::Loop(block, label, ..) => for_each_expr_without_closures(*block, |e| { + match e.kind { + ExprKind::Loop(..) => { + // Unlabeled breaks inside nested loops target the inner loop, not ours + return ControlFlow::Continue(Descend::No); + }, + ExprKind::Break(dest, Some(break_expr)) => { + let targets_this_loop = + dest.label.is_none() || dest.label.map(|l| l.ident) == label.map(|l| l.ident); + if targets_this_loop && has_generic_return_type(cx, break_expr) { + return ControlFlow::Break(()); + } + }, + _ => {}, + } + ControlFlow::Continue(Descend::Yes) + }) + .is_some(), + ExprKind::MethodCall(..) => { + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let ret_ty = sig.output().skip_binder(); + return ret_ty.has_param(); + } + false + }, + ExprKind::Call(callee, _) => { + if let ExprKind::Path(qpath) = &callee.kind { + let res = cx.qpath_res(qpath, callee.hir_id); + if let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) = res { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let ret_ty = sig.output().skip_binder(); + return ret_ty.has_param(); + } + } + false + }, + _ => false, + } +} + +fn is_generic_res(cx: &LateContext<'_>, res: Res) -> bool { + let has_type_params = |def_id| { + cx.tcx + .generics_of(def_id) + .own_params + .iter() + .any(|p| p.kind.is_ty_or_const()) + }; + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => has_type_params(def_id), + // Ctor → Variant → ADT: constructor's parent is variant, variant's parent is the ADT + Res::Def(DefKind::Ctor(..), def_id) => has_type_params(cx.tcx.parent(cx.tcx.parent(def_id))), + _ => false, + } +} + +fn is_cast_in_generic_context<'a>(cx: &LateContext<'a>, cast_expr: &Expr<'a>) -> bool { + let mut current_id = cast_expr.hir_id; + + loop { + let parent_id = cx.tcx.parent_hir_id(current_id); + if parent_id == current_id { + return false; + } + + let parent = cx.tcx.hir_node(parent_id); + + match parent { + rustc_hir::Node::Expr(parent_expr) => { + match &parent_expr.kind { + ExprKind::Closure(_) => return false, + ExprKind::Call(callee, _) => { + if let ExprKind::Path(qpath) = &callee.kind { + let res = cx.qpath_res(qpath, callee.hir_id); + if is_generic_res(cx, res) { + return true; + } + } + }, + ExprKind::MethodCall(..) => { + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) + && cx + .tcx + .generics_of(def_id) + .own_params + .iter() + .any(|p| p.kind.is_ty_or_const()) + { + return true; + } + }, + _ => {}, + } + current_id = parent_id; + }, + _ => return false, + } + } +} + +fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) { + let mut usages = Vec::new(); + + for_each_expr(cx, body.value, |expr| { + if let ExprKind::Path(ref qpath) = expr.kind + && !expr.span.from_expansion() + && let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id) + && id == hir_id + { + let parent_id = cx.tcx.parent_hir_id(expr.hir_id); + let parent = cx.tcx.hir_node(parent_id); + + let usage = if let rustc_hir::Node::Expr(parent_expr) = parent + && let ExprKind::Cast(..) = parent_expr.kind + && !parent_expr.span.from_expansion() + { + UsageInfo { + cast_to: Some(cx.typeck_results().expr_ty(parent_expr)), + in_generic_context: is_cast_in_generic_context(cx, parent_expr), + } + } else { + UsageInfo { + cast_to: None, + in_generic_context: false, + } + }; + usages.push(usage); + } + ControlFlow::<()>::Continue(()) + }); + + let Some(first_target) = usages + .first() + .and_then(|u| u.cast_to) + .filter(|&t| t != binding_info.source_ty) + .filter(|&t| usages.iter().all(|u| u.cast_to == Some(t) && !u.in_generic_context)) + else { + return; + }; + + span_lint_and_sugg( + cx, + NEEDLESS_TYPE_CAST, + binding_info.ty_span, + format!( + "this binding is defined as `{}` but is always cast to `{}`", + binding_info.source_ty, first_target + ), + "consider defining it as", + first_target.to_string(), + Applicability::MaybeIncorrect, + ); +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1a02c2166454..87d75234ebc0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -70,6 +70,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::MANUAL_DANGLING_PTR_INFO, + crate::casts::NEEDLESS_TYPE_CAST_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, diff --git a/tests/ui/needless_type_cast.fixed b/tests/ui/needless_type_cast.fixed new file mode 100644 index 000000000000..32c348d3ca3a --- /dev/null +++ b/tests/ui/needless_type_cast.fixed @@ -0,0 +1,182 @@ +#![warn(clippy::needless_type_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)] + +fn takes_i32(x: i32) -> i32 { + x +} + +fn generic(x: T) -> T { + x +} + +fn main() { + let a: i32 = 10; + //~^ needless_type_cast + let _ = a as i32 + 5; + let _ = a as i32 * 2; + + let b: u16 = 20; + let _ = b; + let _ = b as u32; + + let c: u8 = 5; + let _ = c as u16; + let _ = c as u32; + + let d: i32 = 100; + let _ = d + 1; + + let e = 42u8; + let _ = e as i64; + let _ = e as i64 + 10; + + let f: usize = 1; + //~^ needless_type_cast + let _ = f as usize; +} + +fn test_function_call() { + let a: i32 = 10; + //~^ needless_type_cast + let _ = takes_i32(a as i32); + let _ = takes_i32(a as i32); +} + +fn test_generic_call() { + let a: u8 = 10; + let _ = generic(a as i32); + let _ = generic(a as i32); +} + +fn test_method_on_cast() { + let a: i32 = 10; + //~^ needless_type_cast + let _ = (a as i32).checked_add(5); + let _ = (a as i32).saturating_mul(2); +} + +fn test_iterator_sum() { + let a: i32 = 10; + //~^ needless_type_cast + let arr = [a as i32, a as i32]; + let _: i32 = arr.iter().copied().sum(); +} + +fn test_closure() { + let a: i32 = 10; + //~^ needless_type_cast + let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum(); +} + +fn test_struct_field() { + struct S { + x: i32, + y: i32, + } + + let a: i32 = 10; + //~^ needless_type_cast + let _ = S { + x: a as i32, + y: a as i32, + }; +} + +fn test_option() { + let a: u8 = 10; + let _: Option = Some(a as i32); + let _: Option = Some(a as i32); +} + +fn test_mixed_context() { + let a: u8 = 10; + let _ = takes_i32(a as i32); + let _ = generic(a as i32); +} + +fn test_nested_block() { + if true { + let a: i32 = 10; + //~^ needless_type_cast + let _ = a as i32 + 1; + let _ = a as i32 * 2; + } +} + +fn test_match_expr() { + let a: i32 = 10; + //~^ needless_type_cast + let _ = match 1 { + 1 => a as i32, + _ => a as i32, + }; +} + +fn test_return_expr() -> i32 { + let a: i32 = 10; + //~^ needless_type_cast + a as i32 +} + +fn test_closure_always_cast() { + let a: i32 = 10; + //~^ needless_type_cast + let _ = [1, 2].iter().map(|_| a as i32).sum::(); + let _ = a as i32; +} + +fn test_closure_mixed_usage() { + let a: u8 = 10; + let _ = [1, 2].iter().map(|_| a as i32).sum::(); + let _ = a + 1; +} + +fn test_nested_generic_call() { + let a: u8 = 10; + let _ = generic(takes_i32(a as i32)); + let _ = generic(takes_i32(a as i32)); +} + +fn test_generic_initializer() { + // Should not lint: changing type would affect what generic() returns + let a: u8 = generic(10u8); + let _ = a as i32; + let _ = a as i32; +} + +fn test_unsafe_transmute() { + // Should not lint: initializer contains unsafe block + #[allow(clippy::useless_transmute)] + let x: u32 = unsafe { std::mem::transmute(0u32) }; + let _ = x as u64; +} + +fn test_if_with_generic() { + // Should not lint: one branch has generic return type + let x: u8 = if true { generic(1) } else { 2 }; + let _ = x as i32; +} + +fn test_match_with_generic() { + // Should not lint: one branch has generic return type + let x: u8 = match 1 { + 1 => generic(1), + _ => 2, + }; + let _ = x as i32; +} + +fn test_default() { + // Should not lint: Default::default() has generic return type + let x: u8 = Default::default(); + let _ = x as i32; +} + +fn test_loop_with_generic() { + // Should not lint: loop break has generic return type + #[allow(clippy::never_loop)] + let x: u8 = loop { + break generic(1); + }; + let _ = x as i32; +} diff --git a/tests/ui/needless_type_cast.rs b/tests/ui/needless_type_cast.rs new file mode 100644 index 000000000000..e28f620e035f --- /dev/null +++ b/tests/ui/needless_type_cast.rs @@ -0,0 +1,182 @@ +#![warn(clippy::needless_type_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)] + +fn takes_i32(x: i32) -> i32 { + x +} + +fn generic(x: T) -> T { + x +} + +fn main() { + let a: u8 = 10; + //~^ needless_type_cast + let _ = a as i32 + 5; + let _ = a as i32 * 2; + + let b: u16 = 20; + let _ = b; + let _ = b as u32; + + let c: u8 = 5; + let _ = c as u16; + let _ = c as u32; + + let d: i32 = 100; + let _ = d + 1; + + let e = 42u8; + let _ = e as i64; + let _ = e as i64 + 10; + + let f: u8 = 1; + //~^ needless_type_cast + let _ = f as usize; +} + +fn test_function_call() { + let a: u8 = 10; + //~^ needless_type_cast + let _ = takes_i32(a as i32); + let _ = takes_i32(a as i32); +} + +fn test_generic_call() { + let a: u8 = 10; + let _ = generic(a as i32); + let _ = generic(a as i32); +} + +fn test_method_on_cast() { + let a: u8 = 10; + //~^ needless_type_cast + let _ = (a as i32).checked_add(5); + let _ = (a as i32).saturating_mul(2); +} + +fn test_iterator_sum() { + let a: u8 = 10; + //~^ needless_type_cast + let arr = [a as i32, a as i32]; + let _: i32 = arr.iter().copied().sum(); +} + +fn test_closure() { + let a: u8 = 10; + //~^ needless_type_cast + let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum(); +} + +fn test_struct_field() { + struct S { + x: i32, + y: i32, + } + + let a: u8 = 10; + //~^ needless_type_cast + let _ = S { + x: a as i32, + y: a as i32, + }; +} + +fn test_option() { + let a: u8 = 10; + let _: Option = Some(a as i32); + let _: Option = Some(a as i32); +} + +fn test_mixed_context() { + let a: u8 = 10; + let _ = takes_i32(a as i32); + let _ = generic(a as i32); +} + +fn test_nested_block() { + if true { + let a: u8 = 10; + //~^ needless_type_cast + let _ = a as i32 + 1; + let _ = a as i32 * 2; + } +} + +fn test_match_expr() { + let a: u8 = 10; + //~^ needless_type_cast + let _ = match 1 { + 1 => a as i32, + _ => a as i32, + }; +} + +fn test_return_expr() -> i32 { + let a: u8 = 10; + //~^ needless_type_cast + a as i32 +} + +fn test_closure_always_cast() { + let a: u8 = 10; + //~^ needless_type_cast + let _ = [1, 2].iter().map(|_| a as i32).sum::(); + let _ = a as i32; +} + +fn test_closure_mixed_usage() { + let a: u8 = 10; + let _ = [1, 2].iter().map(|_| a as i32).sum::(); + let _ = a + 1; +} + +fn test_nested_generic_call() { + let a: u8 = 10; + let _ = generic(takes_i32(a as i32)); + let _ = generic(takes_i32(a as i32)); +} + +fn test_generic_initializer() { + // Should not lint: changing type would affect what generic() returns + let a: u8 = generic(10u8); + let _ = a as i32; + let _ = a as i32; +} + +fn test_unsafe_transmute() { + // Should not lint: initializer contains unsafe block + #[allow(clippy::useless_transmute)] + let x: u32 = unsafe { std::mem::transmute(0u32) }; + let _ = x as u64; +} + +fn test_if_with_generic() { + // Should not lint: one branch has generic return type + let x: u8 = if true { generic(1) } else { 2 }; + let _ = x as i32; +} + +fn test_match_with_generic() { + // Should not lint: one branch has generic return type + let x: u8 = match 1 { + 1 => generic(1), + _ => 2, + }; + let _ = x as i32; +} + +fn test_default() { + // Should not lint: Default::default() has generic return type + let x: u8 = Default::default(); + let _ = x as i32; +} + +fn test_loop_with_generic() { + // Should not lint: loop break has generic return type + #[allow(clippy::never_loop)] + let x: u8 = loop { + break generic(1); + }; + let _ = x as i32; +} diff --git a/tests/ui/needless_type_cast.stderr b/tests/ui/needless_type_cast.stderr new file mode 100644 index 000000000000..3ee9df1043e7 --- /dev/null +++ b/tests/ui/needless_type_cast.stderr @@ -0,0 +1,71 @@ +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:13:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + | + = note: `-D clippy::needless-type-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_type_cast)]` + +error: this binding is defined as `u8` but is always cast to `usize` + --> tests/ui/needless_type_cast.rs:33:12 + | +LL | let f: u8 = 1; + | ^^ help: consider defining it as: `usize` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:39:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:52:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:59:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:66:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:77:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:99:16 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:107:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:116:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: this binding is defined as `u8` but is always cast to `i32` + --> tests/ui/needless_type_cast.rs:122:12 + | +LL | let a: u8 = 10; + | ^^ help: consider defining it as: `i32` + +error: aborting due to 11 previous errors + From 9495686c40650b42f25085e807574982073fae89 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 Nov 2025 15:09:29 -0800 Subject: [PATCH 42/56] Update clippy for the ast `TryBlock` change --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 0d809c17989d..af5e3ccb674a 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -545,7 +545,7 @@ fn ident_difference_expr_with_base_location( | (Field(_, _), Field(_, _)) | (AssignOp(_, _, _), AssignOp(_, _, _)) | (Assign(_, _, _), Assign(_, _, _)) - | (TryBlock(_), TryBlock(_)) + | (TryBlock(_, _), TryBlock(_, _)) | (Await(_, _), Await(_, _)) | (Gen(_, _, _, _), Gen(_, _, _, _)) | (Block(_, _), Block(_, _)) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 27b5a57c737d..08663782a1d5 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -199,7 +199,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { ) => eq_label(ll.as_ref(), rl.as_ref()) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), - (TryBlock(l), TryBlock(r)) => eq_block(l, r), + (TryBlock(lb, lt), TryBlock(rb, rt)) => eq_block(lb, rb) && both(lt.as_deref(), rt.as_deref(), eq_ty), (Yield(l), Yield(r)) => eq_expr_opt(l.expr().map(Box::as_ref), r.expr().map(Box::as_ref)) && l.same_kind(r), (Ret(l), Ret(r)) => eq_expr_opt(l.as_deref(), r.as_deref()), (Break(ll, le), Break(rl, re)) => { From c47bc5d1a98d11b8f6c579b3cde5498ca369e02a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 10 Dec 2025 11:00:20 +0100 Subject: [PATCH 43/56] Remove ICE fix that was already backported to 1.91 --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf619d254cec..283c47a368cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,11 +84,6 @@ Current stable, released 2025-12-11 * [`double_parens`] fixed FP when macros are involved [#15420](https://github.com/rust-lang/rust-clippy/pull/15420) -### ICE Fixes - -* [`len_zero`] fixed ICE when fn len has a return type without generic type params - [#15660](https://github.com/rust-lang/rust-clippy/pull/15660) - ## Rust 1.91 Current stable, released 2025-10-30 From c96ff2d42944a2df715115ddeaa6b260695dcca9 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 10 Dec 2025 23:41:19 +0000 Subject: [PATCH 44/56] Remove uses of `cfg(any()/all())` --- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- library/core/src/primitive_docs.rs | 2 +- tests/ui/asm/naked-functions-inline.rs | 2 +- tests/ui/asm/naked-functions-inline.stderr | 6 +- .../unsafe/cfg-unsafe-attributes.rs | 2 +- .../unsafe/extraneous-unsafe-attributes.rs | 4 +- .../extraneous-unsafe-attributes.stderr | 4 +- .../ui/attributes/unsafe/unsafe-attributes.rs | 2 +- .../conditional-compilation-struct-11085.rs | 2 +- tests/ui/cfg/conditional-compile.rs | 2 +- ...nested-cfg-attr-conditional-compilation.rs | 6 +- ...ed-cfg-attr-conditional-compilation.stderr | 4 +- tests/ui/coherence/coherence-cow.rs | 2 +- .../cfg-attr-multi-false.rs | 2 +- .../cfg-attr-multi-true.rs | 2 +- .../conditional-compilation/cfg-attr-parse.rs | 22 +-- .../cfg-attr-parse.stderr | 90 ++++++------ ...-attr-unknown-attribute-macro-expansion.rs | 2 +- ...r-unknown-attribute-macro-expansion.stderr | 6 +- .../cfg-empty-any-all.rs | 12 ++ .../cfg-empty-any-all.stderr | 21 +++ .../conditional-compilation/cfg_attr_path.rs | 4 +- .../ui/conditional-compilation/issue-34028.rs | 2 +- .../module_with_cfg.rs | 2 +- .../ui/coroutine/static-closure-unexpanded.rs | 2 +- .../feature-gate-pin_ergonomics.rs | 2 +- .../feature-gates/feature-gate-super-let.rs | 2 +- .../feature-gate-unsafe-binders.rs | 2 +- .../feature-gate-unsafe_fields.rs | 6 +- .../feature-gate-where_clause_attrs.a.stderr | 132 +++++++++--------- .../feature-gate-where_clause_attrs.b.stderr | 132 +++++++++--------- .../feature-gate-where_clause_attrs.rs | 96 ++++++------- tests/ui/issues/issue-24434.rs | 2 +- tests/ui/lint/issue-97094.rs | 18 +-- tests/ui/lint/issue-97094.stderr | 36 ++--- tests/ui/macros/issue-34171.rs | 2 +- tests/ui/parser/attribute-on-empty.rs | 2 +- tests/ui/parser/attribute-on-empty.stderr | 2 +- tests/ui/parser/attribute-on-type.rs | 6 +- tests/ui/parser/attribute-on-type.stderr | 6 +- .../parser/attribute/attr-pat-struct-rest.rs | 2 +- .../attribute/attr-pat-struct-rest.stderr | 2 +- tests/ui/parser/cfg-keyword-lifetime.rs | 2 +- tests/ui/parser/issue-116781.rs | 2 +- tests/ui/parser/raw/raw-idents.rs | 2 +- .../ty-path-followed-by-single-colon.rs | 2 +- .../ui/proc-macro/ambiguous-builtin-attrs.rs | 2 +- .../proc-macro/ambiguous-builtin-attrs.stderr | 6 +- .../proc-macro/auxiliary/derive-attr-cfg.rs | 2 +- tests/ui/proc-macro/cfg-eval.rs | 8 +- tests/ui/proc-macro/cfg-eval.stderr | 4 +- tests/ui/proc-macro/cfg-eval.stdout | 44 +++--- tests/ui/proc-macro/derive-attr-cfg.rs | 2 +- tests/ui/proc-macro/derive-b.rs | 2 +- .../ui/proc-macro/derive-helper-configured.rs | 4 +- ...gest-import-without-clobbering-attrs.fixed | 2 +- ...suggest-import-without-clobbering-attrs.rs | 2 +- .../unsafe-attributes-fix.fixed | 2 +- .../unsafe-attributes-fix.rs | 2 +- .../unsafe-attributes-fix.stderr | 10 +- tests/ui/static/static-align.rs | 2 +- tests/ui/structs/struct-field-cfg.rs | 16 +-- tests/ui/structs/struct-field-cfg.stderr | 22 +-- tests/ui/test-attrs/issue-34932.rs | 2 +- .../ui/typeck/issue-86721-return-expr-ice.rs | 2 +- 65 files changed, 412 insertions(+), 389 deletions(-) create mode 100644 tests/ui/conditional-compilation/cfg-empty-any-all.rs create mode 100644 tests/ui/conditional-compilation/cfg-empty-any-all.stderr diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index a0bee4e18214..ac5b3c240785 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -167,7 +167,7 @@ pub(crate) fn compile_fn( context.clear(); context.func = codegened_func.func; - #[cfg(any())] // This is never true + #[cfg(false)] let _clif_guard = { use std::fmt::Write; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 15ba72bccaa9..3a4e1e657a3d 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -591,7 +591,7 @@ impl () {} /// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } /// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {} /// # } -/// # #[cfg(any())] +/// # #[cfg(false)] /// #[allow(unused_extern_crates)] /// extern crate libc; /// diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions-inline.rs index 93741f26275b..b6fddc88e19b 100644 --- a/tests/ui/asm/naked-functions-inline.rs +++ b/tests/ui/asm/naked-functions-inline.rs @@ -30,7 +30,7 @@ pub extern "C" fn inline_never() { } #[unsafe(naked)] -#[cfg_attr(all(), inline(never))] +#[cfg_attr(true, inline(never))] //~^ ERROR [E0736] pub extern "C" fn conditional_inline_never() { naked_asm!(""); diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions-inline.stderr index 91140a301edc..785ecf734b9d 100644 --- a/tests/ui/asm/naked-functions-inline.stderr +++ b/tests/ui/asm/naked-functions-inline.stderr @@ -23,12 +23,12 @@ LL | #[inline(never)] | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:33:19 + --> $DIR/naked-functions-inline.rs:33:18 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here -LL | #[cfg_attr(all(), inline(never))] - | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` +LL | #[cfg_attr(true, inline(never))] + | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error: aborting due to 4 previous errors diff --git a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs index 6a9853b2f6fc..7d05e08b2623 100644 --- a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs @@ -1,6 +1,6 @@ //@ build-pass -#[cfg_attr(all(), unsafe(no_mangle))] +#[cfg_attr(true, unsafe(no_mangle))] fn a() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs index 19046c08ca8b..a034b7246a34 100644 --- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs @@ -1,9 +1,9 @@ //@ edition: 2024 -#[unsafe(cfg(any()))] //~ ERROR: is not an unsafe attribute +#[unsafe(cfg(false))] //~ ERROR: is not an unsafe attribute fn a() {} -#[unsafe(cfg_attr(any(), allow(dead_code)))] //~ ERROR: is not an unsafe attribute +#[unsafe(cfg_attr(false, allow(dead_code)))] //~ ERROR: is not an unsafe attribute fn b() {} #[unsafe(test)] //~ ERROR: is not an unsafe attribute diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr index b549a638d5ea..dec8c4d3542b 100644 --- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr @@ -1,7 +1,7 @@ error: `cfg` is not an unsafe attribute --> $DIR/extraneous-unsafe-attributes.rs:3:3 | -LL | #[unsafe(cfg(any()))] +LL | #[unsafe(cfg(false))] | ^^^^^^ this is not an unsafe attribute | = note: extraneous unsafe is not allowed in attributes @@ -9,7 +9,7 @@ LL | #[unsafe(cfg(any()))] error: `cfg_attr` is not an unsafe attribute --> $DIR/extraneous-unsafe-attributes.rs:6:3 | -LL | #[unsafe(cfg_attr(any(), allow(dead_code)))] +LL | #[unsafe(cfg_attr(false, allow(dead_code)))] | ^^^^^^ this is not an unsafe attribute | = note: extraneous unsafe is not allowed in attributes diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs index 5c57767b3b96..c08b0ed995e5 100644 --- a/tests/ui/attributes/unsafe/unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs @@ -6,7 +6,7 @@ fn a() {} #[unsafe(export_name = "foo")] fn b() {} -#[cfg_attr(any(), unsafe(no_mangle))] +#[cfg_attr(false, unsafe(no_mangle))] static VAR2: u32 = 1; fn main() {} diff --git a/tests/ui/cfg/conditional-compilation-struct-11085.rs b/tests/ui/cfg/conditional-compilation-struct-11085.rs index cd6dded54d30..8fdc88be37d2 100644 --- a/tests/ui/cfg/conditional-compilation-struct-11085.rs +++ b/tests/ui/cfg/conditional-compilation-struct-11085.rs @@ -11,7 +11,7 @@ struct Foo { } struct Foo2 { - #[cfg(all())] + #[cfg(true)] foo: isize, } diff --git a/tests/ui/cfg/conditional-compile.rs b/tests/ui/cfg/conditional-compile.rs index 0739e877bfd1..8761197891f5 100644 --- a/tests/ui/cfg/conditional-compile.rs +++ b/tests/ui/cfg/conditional-compile.rs @@ -151,5 +151,5 @@ mod test_methods { } } -#[cfg(any())] +#[cfg(false)] mod nonexistent_file; // Check that unconfigured non-inline modules are not loaded or parsed. diff --git a/tests/ui/cfg/nested-cfg-attr-conditional-compilation.rs b/tests/ui/cfg/nested-cfg-attr-conditional-compilation.rs index c5d86a27d522..8e79ce8d1546 100644 --- a/tests/ui/cfg/nested-cfg-attr-conditional-compilation.rs +++ b/tests/ui/cfg/nested-cfg-attr-conditional-compilation.rs @@ -3,14 +3,14 @@ //! expansion works from outside to inside, eventually applying the innermost //! conditional compilation directive. //! -//! In this test, `cfg_attr(all(), cfg_attr(all(), cfg(false)))` should expand to: -//! 1. `cfg_attr(all(), cfg(false))` (outer cfg_attr applied) +//! In this test, `cfg_attr(true, cfg_attr(true, cfg(false)))` should expand to: +//! 1. `cfg_attr(true, cfg(false))` (outer cfg_attr applied) //! 2. `cfg(false)` (inner cfg_attr applied) //! 3. Function `f` is excluded from compilation //! //! Added in . -#[cfg_attr(all(), cfg_attr(all(), cfg(false)))] //~ NOTE the item is gated here +#[cfg_attr(true, cfg_attr(true, cfg(false)))] //~ NOTE the item is gated here fn f() {} //~ NOTE found an item that was configured out fn main() { diff --git a/tests/ui/cfg/nested-cfg-attr-conditional-compilation.stderr b/tests/ui/cfg/nested-cfg-attr-conditional-compilation.stderr index 3f833bd558b8..e93a5433d979 100644 --- a/tests/ui/cfg/nested-cfg-attr-conditional-compilation.stderr +++ b/tests/ui/cfg/nested-cfg-attr-conditional-compilation.stderr @@ -7,8 +7,8 @@ LL | f() note: found an item that was configured out --> $DIR/nested-cfg-attr-conditional-compilation.rs:14:4 | -LL | #[cfg_attr(all(), cfg_attr(all(), cfg(false)))] - | ----- the item is gated here +LL | #[cfg_attr(true, cfg_attr(true, cfg(false)))] + | ----- the item is gated here LL | fn f() {} | ^ diff --git a/tests/ui/coherence/coherence-cow.rs b/tests/ui/coherence/coherence-cow.rs index af94964762a9..2fc33e3a3e8f 100644 --- a/tests/ui/coherence/coherence-cow.rs +++ b/tests/ui/coherence/coherence-cow.rs @@ -1,6 +1,6 @@ //@ revisions: re_a re_b re_c -#![cfg_attr(any(), re_a, re_b, re_c)] +#![cfg_attr(false, re_a, re_b, re_c)] //@ aux-build:coherence_lib.rs diff --git a/tests/ui/conditional-compilation/cfg-attr-multi-false.rs b/tests/ui/conditional-compilation/cfg-attr-multi-false.rs index cfb430ec5b23..871c1b81acd2 100644 --- a/tests/ui/conditional-compilation/cfg-attr-multi-false.rs +++ b/tests/ui/conditional-compilation/cfg-attr-multi-false.rs @@ -5,7 +5,7 @@ #![warn(unused_must_use)] -#[cfg_attr(any(), deprecated, must_use)] +#[cfg_attr(false, deprecated, must_use)] struct Struct {} impl Struct { diff --git a/tests/ui/conditional-compilation/cfg-attr-multi-true.rs b/tests/ui/conditional-compilation/cfg-attr-multi-true.rs index 424760c2e663..24950c8d4234 100644 --- a/tests/ui/conditional-compilation/cfg-attr-multi-true.rs +++ b/tests/ui/conditional-compilation/cfg-attr-multi-true.rs @@ -6,7 +6,7 @@ #![warn(unused_must_use)] -#[cfg_attr(all(), deprecated, must_use)] +#[cfg_attr(true, deprecated, must_use)] struct MustUseDeprecated {} impl MustUseDeprecated { //~ warning: use of deprecated diff --git a/tests/ui/conditional-compilation/cfg-attr-parse.rs b/tests/ui/conditional-compilation/cfg-attr-parse.rs index b8aaad2685ef..21df51264e2d 100644 --- a/tests/ui/conditional-compilation/cfg-attr-parse.rs +++ b/tests/ui/conditional-compilation/cfg-attr-parse.rs @@ -5,50 +5,50 @@ struct NoConfigurationPredicate; // Zero attributes, zero trailing comma (comma manatory here) -#[cfg_attr(all())] //~ error: expected `,`, found end of `cfg_attr` +#[cfg_attr(true)] //~ error: expected `,`, found end of `cfg_attr` struct A0C0; // Zero attributes, one trailing comma -#[cfg_attr(all(),)] +#[cfg_attr(true,)] //~^ WARN `#[cfg_attr]` does not expand to any attributes struct A0C1; // Zero attributes, two trailing commas -#[cfg_attr(all(),,)] //~ ERROR expected identifier +#[cfg_attr(true,,)] //~ ERROR expected identifier struct A0C2; // One attribute, no trailing comma -#[cfg_attr(all(), must_use)] // Ok +#[cfg_attr(true, must_use)] // Ok struct A1C0; // One attribute, one trailing comma -#[cfg_attr(all(), must_use,)] // Ok +#[cfg_attr(true, must_use,)] // Ok struct A1C1; // One attribute, two trailing commas -#[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier +#[cfg_attr(true, must_use,,)] //~ ERROR expected identifier struct A1C2; // Two attributes, no trailing comma -#[cfg_attr(all(), must_use, deprecated)] // Ok +#[cfg_attr(true, must_use, deprecated)] // Ok struct A2C0; // Two attributes, one trailing comma -#[cfg_attr(all(), must_use, deprecated,)] // Ok +#[cfg_attr(true, must_use, deprecated,)] // Ok struct A2C1; // Two attributes, two trailing commas -#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier +#[cfg_attr(true, must_use, deprecated,,)] //~ ERROR expected identifier struct A2C2; // Wrong delimiter `[` -#[cfg_attr[all(),,]] +#[cfg_attr[true,,]] //~^ ERROR wrong `cfg_attr` delimiters //~| ERROR expected identifier, found `,` struct BracketZero; // Wrong delimiter `{` -#[cfg_attr{all(),,}] +#[cfg_attr{true,,}] //~^ ERROR wrong `cfg_attr` delimiters //~| ERROR expected identifier, found `,` struct BraceZero; diff --git a/tests/ui/conditional-compilation/cfg-attr-parse.stderr b/tests/ui/conditional-compilation/cfg-attr-parse.stderr index 4d4769b05cda..8dbe8969fd1c 100644 --- a/tests/ui/conditional-compilation/cfg-attr-parse.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-parse.stderr @@ -10,45 +10,45 @@ LL | #[cfg_attr()] = note: for more information, visit error: expected `,`, found end of `cfg_attr` input - --> $DIR/cfg-attr-parse.rs:8:17 + --> $DIR/cfg-attr-parse.rs:8:16 | -LL | #[cfg_attr(all())] - | ----------------^- +LL | #[cfg_attr(true)] + | ---------------^- + | | | + | | expected `,` + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error: expected identifier, found `,` + --> $DIR/cfg-attr-parse.rs:17:17 + | +LL | #[cfg_attr(true,,)] + | ----------------^-- | | | - | | expected `,` + | | expected identifier | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` | = note: for more information, visit error: expected identifier, found `,` - --> $DIR/cfg-attr-parse.rs:17:18 + --> $DIR/cfg-attr-parse.rs:29:27 | -LL | #[cfg_attr(all(),,)] - | -----------------^-- - | | | - | | expected identifier +LL | #[cfg_attr(true, must_use,,)] + | --------------------------^-- + | | | + | | expected identifier | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` | = note: for more information, visit error: expected identifier, found `,` - --> $DIR/cfg-attr-parse.rs:29:28 + --> $DIR/cfg-attr-parse.rs:41:39 | -LL | #[cfg_attr(all(), must_use,,)] - | ---------------------------^-- - | | | - | | expected identifier - | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` - | - = note: for more information, visit - -error: expected identifier, found `,` - --> $DIR/cfg-attr-parse.rs:41:40 - | -LL | #[cfg_attr(all(), must_use, deprecated,,)] - | ---------------------------------------^-- - | | | - | | expected identifier +LL | #[cfg_attr(true, must_use, deprecated,,)] + | --------------------------------------^-- + | | | + | | expected identifier | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` | = note: for more information, visit @@ -56,22 +56,22 @@ LL | #[cfg_attr(all(), must_use, deprecated,,)] error: wrong `cfg_attr` delimiters --> $DIR/cfg-attr-parse.rs:45:11 | -LL | #[cfg_attr[all(),,]] - | ^^^^^^^^^ +LL | #[cfg_attr[true,,]] + | ^^^^^^^^ | help: the delimiters should be `(` and `)` | -LL - #[cfg_attr[all(),,]] -LL + #[cfg_attr(all(),,)] +LL - #[cfg_attr[true,,]] +LL + #[cfg_attr(true,,)] | error: expected identifier, found `,` - --> $DIR/cfg-attr-parse.rs:45:18 + --> $DIR/cfg-attr-parse.rs:45:17 | -LL | #[cfg_attr[all(),,]] - | -----------------^-- - | | | - | | expected identifier +LL | #[cfg_attr[true,,]] + | ----------------^-- + | | | + | | expected identifier | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` | = note: for more information, visit @@ -79,22 +79,22 @@ LL | #[cfg_attr[all(),,]] error: wrong `cfg_attr` delimiters --> $DIR/cfg-attr-parse.rs:51:11 | -LL | #[cfg_attr{all(),,}] - | ^^^^^^^^^ +LL | #[cfg_attr{true,,}] + | ^^^^^^^^ | help: the delimiters should be `(` and `)` | -LL - #[cfg_attr{all(),,}] -LL + #[cfg_attr(all(),,)] +LL - #[cfg_attr{true,,}] +LL + #[cfg_attr(true,,)] | error: expected identifier, found `,` - --> $DIR/cfg-attr-parse.rs:51:18 + --> $DIR/cfg-attr-parse.rs:51:17 | -LL | #[cfg_attr{all(),,}] - | -----------------^-- - | | | - | | expected identifier +LL | #[cfg_attr{true,,}] + | ----------------^-- + | | | + | | expected identifier | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` | = note: for more information, visit @@ -102,8 +102,8 @@ LL | #[cfg_attr{all(),,}] warning: `#[cfg_attr]` does not expand to any attributes --> $DIR/cfg-attr-parse.rs:12:1 | -LL | #[cfg_attr(all(),)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true,)] + | ^^^^^^^^^^^^^^^^^^ | = note: requested on the command line with `-W unused-attributes` diff --git a/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs b/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs index 45b757e92830..0305be5d24e6 100644 --- a/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs +++ b/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs @@ -1,6 +1,6 @@ macro_rules! foo { () => { - #[cfg_attr(all(), unknown)] + #[cfg_attr(true, unknown)] //~^ ERROR cannot find attribute `unknown` in this scope fn foo() {} } diff --git a/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr b/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr index c91ad128d6e5..bdddbd68cdaf 100644 --- a/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr @@ -1,8 +1,8 @@ error: cannot find attribute `unknown` in this scope - --> $DIR/cfg-attr-unknown-attribute-macro-expansion.rs:3:27 + --> $DIR/cfg-attr-unknown-attribute-macro-expansion.rs:3:26 | -LL | #[cfg_attr(all(), unknown)] - | ^^^^^^^ +LL | #[cfg_attr(true, unknown)] + | ^^^^^^^ ... LL | foo!(); | ------ in this macro invocation diff --git a/tests/ui/conditional-compilation/cfg-empty-any-all.rs b/tests/ui/conditional-compilation/cfg-empty-any-all.rs new file mode 100644 index 000000000000..48ed4342235c --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-empty-any-all.rs @@ -0,0 +1,12 @@ +//! Test the behaviour of `cfg(any())` and `cfg(all())` + +#[cfg(any())] // Equivalent to cfg(false) +struct Disabled; + +#[cfg(all())] // Equivalent to cfg(true) +struct Enabled; + +fn main() { + let _ = Disabled; //~ ERROR: cannot find value `Disabled` + let _ = Enabled; // ok +} diff --git a/tests/ui/conditional-compilation/cfg-empty-any-all.stderr b/tests/ui/conditional-compilation/cfg-empty-any-all.stderr new file mode 100644 index 000000000000..1674f2def23a --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-empty-any-all.stderr @@ -0,0 +1,21 @@ +error[E0425]: cannot find value `Disabled` in this scope + --> $DIR/cfg-empty-any-all.rs:10:13 + | +LL | let _ = Disabled; + | ^^^^^^^^ not found in this scope + | +note: found an item that was configured out + --> $DIR/cfg-empty-any-all.rs:4:8 + | +LL | #[cfg(any())] // Equivalent to cfg(false) + | -- the item is gated here +LL | struct Disabled; + | ^^^^^^^^ +help: consider importing this unit variant + | +LL + use std::backtrace::BacktraceStatus::Disabled; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/conditional-compilation/cfg_attr_path.rs b/tests/ui/conditional-compilation/cfg_attr_path.rs index 00e07761977a..f300e02932b7 100644 --- a/tests/ui/conditional-compilation/cfg_attr_path.rs +++ b/tests/ui/conditional-compilation/cfg_attr_path.rs @@ -3,8 +3,8 @@ #![deny(unused_attributes)] // c.f #35584 mod auxiliary { - #[cfg_attr(any(), path = "nonexistent_file.rs")] pub mod namespaced_enums; - #[cfg_attr(all(), path = "namespaced_enums.rs")] pub mod nonexistent_file; + #[cfg_attr(false, path = "nonexistent_file.rs")] pub mod namespaced_enums; + #[cfg_attr(true, path = "namespaced_enums.rs")] pub mod nonexistent_file; } fn main() { diff --git a/tests/ui/conditional-compilation/issue-34028.rs b/tests/ui/conditional-compilation/issue-34028.rs index 3ee43cb4b322..d6f3f7a9abce 100644 --- a/tests/ui/conditional-compilation/issue-34028.rs +++ b/tests/ui/conditional-compilation/issue-34028.rs @@ -1,7 +1,7 @@ //@ check-pass macro_rules! m { - () => { #[cfg(any())] fn f() {} } + () => { #[cfg(false)] fn f() {} } } trait T {} diff --git a/tests/ui/conditional-compilation/module_with_cfg.rs b/tests/ui/conditional-compilation/module_with_cfg.rs index a96f8a3e6e96..32486bdb43d6 100644 --- a/tests/ui/conditional-compilation/module_with_cfg.rs +++ b/tests/ui/conditional-compilation/module_with_cfg.rs @@ -1,3 +1,3 @@ //@ ignore-auxiliary (used by `./inner-cfg-non-inline-mod.rs`) -#![cfg_attr(all(), cfg(false))] +#![cfg_attr(true, cfg(false))] diff --git a/tests/ui/coroutine/static-closure-unexpanded.rs b/tests/ui/coroutine/static-closure-unexpanded.rs index 7cf24774deda..ac7c251c8348 100644 --- a/tests/ui/coroutine/static-closure-unexpanded.rs +++ b/tests/ui/coroutine/static-closure-unexpanded.rs @@ -1,7 +1,7 @@ // Tests that static closures are not stable in the parser grammar unless the // coroutine feature is enabled. -#[cfg(any())] +#[cfg(false)] fn foo() { let _ = static || {}; //~^ ERROR coroutine syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 7f669d0b93e5..6cb4492c3754 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -58,7 +58,7 @@ fn patterns<'a>( ref pin const w: i32, //~ ERROR pinned reference syntax is experimental ) {} -#[cfg(any())] +#[cfg(false)] mod not_compiled { use std::pin::Pin; diff --git a/tests/ui/feature-gates/feature-gate-super-let.rs b/tests/ui/feature-gates/feature-gate-super-let.rs index 7be080039133..19da1c4aa39b 100644 --- a/tests/ui/feature-gates/feature-gate-super-let.rs +++ b/tests/ui/feature-gates/feature-gate-super-let.rs @@ -4,7 +4,7 @@ fn main() { } // Check that it also isn't accepted in cfg'd out code. -#[cfg(any())] +#[cfg(false)] fn a() { super let a = 1; //~^ ERROR `super let` is experimental diff --git a/tests/ui/feature-gates/feature-gate-unsafe-binders.rs b/tests/ui/feature-gates/feature-gate-unsafe-binders.rs index a2997ced4fa1..e1eda7def482 100644 --- a/tests/ui/feature-gates/feature-gate-unsafe-binders.rs +++ b/tests/ui/feature-gates/feature-gate-unsafe-binders.rs @@ -1,4 +1,4 @@ -#[cfg(any())] +#[cfg(false)] fn test() { let x: unsafe<> (); //~^ ERROR unsafe binder types are experimental diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.rs b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs index 8f9b411df469..2b0bbaa08357 100644 --- a/tests/ui/feature-gates/feature-gate-unsafe_fields.rs +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs @@ -4,7 +4,7 @@ #![cfg_attr(with_gate, feature(unsafe_fields))] //[with_gate]~ WARNING -#[cfg(any())] +#[cfg(false)] struct Foo { unsafe field: (), //[without_gate]~ ERROR } @@ -12,14 +12,14 @@ struct Foo { // This should not parse as an unsafe field definition. struct FooTuple(unsafe fn()); -#[cfg(any())] +#[cfg(false)] enum Bar { Variant { unsafe field: () }, //[without_gate]~ ERROR // This should not parse as an unsafe field definition. VariantTuple(unsafe fn()), } -#[cfg(any())] +#[cfg(false)] union Baz { unsafe field: (), //[without_gate]~ ERROR } diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr b/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr index 6a8f01bbcce1..970763157d18 100644 --- a/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr @@ -21,8 +21,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:35:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -31,7 +31,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:36:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -61,8 +61,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:39:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -71,7 +71,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:40:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -101,8 +101,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:46:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -111,7 +111,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:47:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -141,8 +141,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:50:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -151,7 +151,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:51:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -181,8 +181,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:57:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -191,7 +191,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:58:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -221,8 +221,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:61:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -231,7 +231,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:62:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -261,8 +261,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:69:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -271,7 +271,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:70:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -301,8 +301,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:73:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -311,7 +311,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:74:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -341,8 +341,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:79:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -351,7 +351,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:80:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -381,8 +381,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:83:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -391,7 +391,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:84:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -421,8 +421,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:90:9 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -431,7 +431,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:91:9 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -461,8 +461,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:94:9 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -471,7 +471,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:95:9 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -501,8 +501,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:103:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -511,7 +511,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:104:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -541,8 +541,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:107:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -551,7 +551,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:108:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -581,8 +581,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:117:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -591,7 +591,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:118:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -621,8 +621,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:121:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -631,7 +631,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:122:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -661,8 +661,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:132:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -671,7 +671,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:133:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -701,8 +701,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:136:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -711,7 +711,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:137:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -741,8 +741,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:145:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -751,7 +751,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:146:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -781,8 +781,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:149:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -791,7 +791,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:150:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -821,8 +821,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:155:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -831,7 +831,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:156:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -861,8 +861,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:159:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -871,7 +871,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:160:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr b/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr index 6a8f01bbcce1..970763157d18 100644 --- a/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr @@ -21,8 +21,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:35:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -31,7 +31,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:36:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -61,8 +61,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:39:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -71,7 +71,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:40:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -101,8 +101,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:46:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -111,7 +111,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:47:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -141,8 +141,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:50:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -151,7 +151,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:51:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -181,8 +181,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:57:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -191,7 +191,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:58:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -221,8 +221,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:61:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -231,7 +231,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:62:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -261,8 +261,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:69:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -271,7 +271,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:70:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -301,8 +301,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:73:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -311,7 +311,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:74:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -341,8 +341,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:79:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -351,7 +351,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:80:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -381,8 +381,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:83:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -391,7 +391,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:84:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -421,8 +421,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:90:9 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -431,7 +431,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:91:9 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -461,8 +461,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:94:9 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -471,7 +471,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:95:9 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -501,8 +501,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:103:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -511,7 +511,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:104:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -541,8 +541,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:107:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -551,7 +551,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:108:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -581,8 +581,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:117:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -591,7 +591,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:118:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -621,8 +621,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:121:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -631,7 +631,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:122:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -661,8 +661,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:132:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -671,7 +671,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:133:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -701,8 +701,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:136:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -711,7 +711,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:137:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -741,8 +741,8 @@ LL | #[cfg(b)] T: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:145:5 | -LL | #[cfg(all())] T: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] T: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -751,7 +751,7 @@ LL | #[cfg(all())] T: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:146:5 | -LL | #[cfg(any())] T: TraitAny, +LL | #[cfg(false)] T: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -781,8 +781,8 @@ LL | #[cfg_attr(b, cfg(b))] T: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:149:5 | -LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -791,7 +791,7 @@ LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:150:5 | -LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] T: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -821,8 +821,8 @@ LL | #[cfg(b)] U: TraitB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:155:9 | -LL | #[cfg(all())] U: TraitAll, - | ^^^^^^^^^^^^^ +LL | #[cfg(true)] U: TraitAll, + | ^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -831,7 +831,7 @@ LL | #[cfg(all())] U: TraitAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:156:9 | -LL | #[cfg(any())] U: TraitAny, +LL | #[cfg(false)] U: TraitAny, | ^^^^^^^^^^^^^ | = note: see issue #115590 for more information @@ -861,8 +861,8 @@ LL | #[cfg_attr(b, cfg(b))] U: TraitBB, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:159:9 | -LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, cfg(true))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable @@ -871,7 +871,7 @@ LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, error[E0658]: attributes in `where` clause are unstable --> $DIR/feature-gate-where_clause_attrs.rs:160:9 | -LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, +LL | #[cfg_attr(false, cfg(false))] U: TraitAnyAny, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #115590 for more information diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs b/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs index 09c734a52de0..b87a1d56ea06 100644 --- a/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs @@ -15,16 +15,16 @@ trait TraitAA {} #[cfg_attr(b, cfg(b))] trait TraitBB {} -#[cfg(all())] +#[cfg(true)] trait TraitAll {} -#[cfg(any())] +#[cfg(false)] trait TraitAny {} -#[cfg_attr(all(), cfg(all()))] +#[cfg_attr(true, cfg(true))] trait TraitAllAll {} -#[cfg_attr(any(), cfg(any()))] +#[cfg_attr(false, cfg(false))] trait TraitAnyAny {} @@ -32,67 +32,67 @@ trait A where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { type B where #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] U: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable fn foo(&self) where #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] U: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable } impl A for T where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { type B = () where #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] U: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable fn foo(&self) where #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable {} } @@ -100,12 +100,12 @@ struct C where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { _t: PhantomData, } @@ -114,12 +114,12 @@ union D where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { _t: PhantomData, @@ -129,12 +129,12 @@ enum E where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { E(PhantomData), } @@ -142,21 +142,21 @@ where impl C where #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] T: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable { fn new() where #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable - #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable - #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg(true)] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(false)] U: TraitAny, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable - #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(true, cfg(true))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(false, cfg(false))] U: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable {} } diff --git a/tests/ui/issues/issue-24434.rs b/tests/ui/issues/issue-24434.rs index 991084c27409..429bcf4a8d87 100644 --- a/tests/ui/issues/issue-24434.rs +++ b/tests/ui/issues/issue-24434.rs @@ -1,6 +1,6 @@ //@ check-pass -#![cfg_attr(all(), feature(rustc_attrs))] +#![cfg_attr(true, feature(rustc_attrs))] #![rustc_dummy] fn main() {} diff --git a/tests/ui/lint/issue-97094.rs b/tests/ui/lint/issue-97094.rs index 22525ca11ae0..737f2827b67a 100644 --- a/tests/ui/lint/issue-97094.rs +++ b/tests/ui/lint/issue-97094.rs @@ -2,19 +2,19 @@ // Ensure that unknown lints inside cfg-attr's are linted for -#![cfg_attr(all(), allow(nonex_lint_top_level))] +#![cfg_attr(true, allow(nonex_lint_top_level))] //~^ ERROR unknown lint -#![cfg_attr(all(), allow(bare_trait_object))] +#![cfg_attr(true, allow(bare_trait_object))] //~^ ERROR has been renamed -#[cfg_attr(all(), allow(nonex_lint_mod))] +#[cfg_attr(true, allow(nonex_lint_mod))] //~^ ERROR unknown lint mod baz { - #![cfg_attr(all(), allow(nonex_lint_mod_inner))] + #![cfg_attr(true, allow(nonex_lint_mod_inner))] //~^ ERROR unknown lint } -#[cfg_attr(all(), allow(nonex_lint_fn))] +#[cfg_attr(true, allow(nonex_lint_fn))] //~^ ERROR unknown lint pub fn main() {} @@ -25,24 +25,24 @@ macro_rules! bar { } bar!( - #[cfg_attr(all(), allow(nonex_lint_in_macro))] + #[cfg_attr(true, allow(nonex_lint_in_macro))] //~^ ERROR unknown lint pub fn _bar() {} ); // No warning for non-applying cfg -#[cfg_attr(any(), allow(nonex_lint_fn))] +#[cfg_attr(false, allow(nonex_lint_fn))] pub fn _foo() {} // Allowing unknown lints works if inside cfg_attr -#[cfg_attr(all(), allow(unknown_lints))] +#[cfg_attr(true, allow(unknown_lints))] mod bar_allowed { #[allow(nonex_lint_fn)] fn _foo() {} } // ... but not if the cfg_attr doesn't evaluate -#[cfg_attr(any(), allow(unknown_lints))] +#[cfg_attr(false, allow(unknown_lints))] mod bar_not_allowed { #[allow(nonex_lint_fn)] //~^ ERROR unknown lint diff --git a/tests/ui/lint/issue-97094.stderr b/tests/ui/lint/issue-97094.stderr index 1a0a3eaf2507..e12250aa7542 100644 --- a/tests/ui/lint/issue-97094.stderr +++ b/tests/ui/lint/issue-97094.stderr @@ -1,8 +1,8 @@ error: unknown lint: `nonex_lint_top_level` - --> $DIR/issue-97094.rs:5:26 + --> $DIR/issue-97094.rs:5:25 | -LL | #![cfg_attr(all(), allow(nonex_lint_top_level))] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #![cfg_attr(true, allow(nonex_lint_top_level))] + | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/issue-97094.rs:1:9 @@ -12,36 +12,36 @@ LL | #![deny(warnings)] = note: `#[deny(unknown_lints)]` implied by `#[deny(warnings)]` error: lint `bare_trait_object` has been renamed to `bare_trait_objects` - --> $DIR/issue-97094.rs:7:26 + --> $DIR/issue-97094.rs:7:25 | -LL | #![cfg_attr(all(), allow(bare_trait_object))] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `bare_trait_objects` +LL | #![cfg_attr(true, allow(bare_trait_object))] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `bare_trait_objects` | = note: `#[deny(renamed_and_removed_lints)]` implied by `#[deny(warnings)]` error: unknown lint: `nonex_lint_mod` - --> $DIR/issue-97094.rs:10:25 + --> $DIR/issue-97094.rs:10:24 | -LL | #[cfg_attr(all(), allow(nonex_lint_mod))] - | ^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, allow(nonex_lint_mod))] + | ^^^^^^^^^^^^^^ error: unknown lint: `nonex_lint_mod_inner` - --> $DIR/issue-97094.rs:13:30 + --> $DIR/issue-97094.rs:13:29 | -LL | #![cfg_attr(all(), allow(nonex_lint_mod_inner))] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #![cfg_attr(true, allow(nonex_lint_mod_inner))] + | ^^^^^^^^^^^^^^^^^^^^ error: unknown lint: `nonex_lint_fn` - --> $DIR/issue-97094.rs:17:25 + --> $DIR/issue-97094.rs:17:24 | -LL | #[cfg_attr(all(), allow(nonex_lint_fn))] - | ^^^^^^^^^^^^^ +LL | #[cfg_attr(true, allow(nonex_lint_fn))] + | ^^^^^^^^^^^^^ error: unknown lint: `nonex_lint_in_macro` - --> $DIR/issue-97094.rs:28:29 + --> $DIR/issue-97094.rs:28:28 | -LL | #[cfg_attr(all(), allow(nonex_lint_in_macro))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(true, allow(nonex_lint_in_macro))] + | ^^^^^^^^^^^^^^^^^^^ error: unknown lint: `nonex_lint_fn` --> $DIR/issue-97094.rs:47:13 diff --git a/tests/ui/macros/issue-34171.rs b/tests/ui/macros/issue-34171.rs index fbc2ea50097d..3f13341230fa 100644 --- a/tests/ui/macros/issue-34171.rs +++ b/tests/ui/macros/issue-34171.rs @@ -6,5 +6,5 @@ macro_rules! apply_null { } fn main() { - apply_null!(#[cfg(all())] fn f() {}); + apply_null!(#[cfg(true)] fn f() {}); } diff --git a/tests/ui/parser/attribute-on-empty.rs b/tests/ui/parser/attribute-on-empty.rs index 5932377f73ec..0177e6c1b59d 100644 --- a/tests/ui/parser/attribute-on-empty.rs +++ b/tests/ui/parser/attribute-on-empty.rs @@ -4,7 +4,7 @@ struct Baz(i32); fn main() { - let _: Baz<#[cfg(any())]> = todo!(); + let _: Baz<#[cfg(false)]> = todo!(); //~^ ERROR attributes cannot be applied here } diff --git a/tests/ui/parser/attribute-on-empty.stderr b/tests/ui/parser/attribute-on-empty.stderr index 7c4806c8704a..6bcbf1ceb8d1 100644 --- a/tests/ui/parser/attribute-on-empty.stderr +++ b/tests/ui/parser/attribute-on-empty.stderr @@ -1,7 +1,7 @@ error: attributes cannot be applied here --> $DIR/attribute-on-empty.rs:7:16 | -LL | let _: Baz<#[cfg(any())]> = todo!(); +LL | let _: Baz<#[cfg(false)]> = todo!(); | - ^^^^^^^^^^^^^ attributes are not allowed here | | | while parsing the type for `_` diff --git a/tests/ui/parser/attribute-on-type.rs b/tests/ui/parser/attribute-on-type.rs index 196d322bdf8f..b400bd1c173f 100644 --- a/tests/ui/parser/attribute-on-type.rs +++ b/tests/ui/parser/attribute-on-type.rs @@ -16,16 +16,16 @@ fn main() { let _: #[attr] &'static str = "123"; //~^ ERROR attributes cannot be applied to types - let _: Bar<#[cfg(any())] 'static> = Bar(&123); + let _: Bar<#[cfg(false)] 'static> = Bar(&123); //~^ ERROR attributes cannot be applied to generic arguments - let _: Baz<#[cfg(any())] 42> = Baz(42); + let _: Baz<#[cfg(false)] 42> = Baz(42); //~^ ERROR attributes cannot be applied to generic arguments let _: Foo<#[cfg(not(wrong))]String> = Foo(String::new()); //~^ ERROR attributes cannot be applied to generic arguments - let _: Bar<#[cfg(any())] 'static> = Bar(&456); + let _: Bar<#[cfg(false)] 'static> = Bar(&456); //~^ ERROR attributes cannot be applied to generic arguments let _generic: Box<#[attr] i32> = Box::new(1); diff --git a/tests/ui/parser/attribute-on-type.stderr b/tests/ui/parser/attribute-on-type.stderr index 603c7e2be51a..316620325c04 100644 --- a/tests/ui/parser/attribute-on-type.stderr +++ b/tests/ui/parser/attribute-on-type.stderr @@ -13,13 +13,13 @@ LL | let _: #[attr] &'static str = "123"; error: attributes cannot be applied to generic arguments --> $DIR/attribute-on-type.rs:19:16 | -LL | let _: Bar<#[cfg(any())] 'static> = Bar(&123); +LL | let _: Bar<#[cfg(false)] 'static> = Bar(&123); | ^^^^^^^^^^^^^ attributes are not allowed here error: attributes cannot be applied to generic arguments --> $DIR/attribute-on-type.rs:22:16 | -LL | let _: Baz<#[cfg(any())] 42> = Baz(42); +LL | let _: Baz<#[cfg(false)] 42> = Baz(42); | ^^^^^^^^^^^^^ attributes are not allowed here error: attributes cannot be applied to generic arguments @@ -31,7 +31,7 @@ LL | let _: Foo<#[cfg(not(wrong))]String> = Foo(String::new()); error: attributes cannot be applied to generic arguments --> $DIR/attribute-on-type.rs:28:16 | -LL | let _: Bar<#[cfg(any())] 'static> = Bar(&456); +LL | let _: Bar<#[cfg(false)] 'static> = Bar(&456); | ^^^^^^^^^^^^^ attributes are not allowed here error: attributes cannot be applied to generic arguments diff --git a/tests/ui/parser/attribute/attr-pat-struct-rest.rs b/tests/ui/parser/attribute/attr-pat-struct-rest.rs index b2bfcf82df8d..8f0e4cd827e0 100644 --- a/tests/ui/parser/attribute/attr-pat-struct-rest.rs +++ b/tests/ui/parser/attribute/attr-pat-struct-rest.rs @@ -3,6 +3,6 @@ struct S {} fn main() { - let S { #[cfg(any())] .. } = S {}; + let S { #[cfg(false)] .. } = S {}; //~^ ERROR expected identifier, found `..` } diff --git a/tests/ui/parser/attribute/attr-pat-struct-rest.stderr b/tests/ui/parser/attribute/attr-pat-struct-rest.stderr index f72c54973fce..94ad7d571101 100644 --- a/tests/ui/parser/attribute/attr-pat-struct-rest.stderr +++ b/tests/ui/parser/attribute/attr-pat-struct-rest.stderr @@ -1,7 +1,7 @@ error: expected identifier, found `..` --> $DIR/attr-pat-struct-rest.rs:6:27 | -LL | let S { #[cfg(any())] .. } = S {}; +LL | let S { #[cfg(false)] .. } = S {}; | - ^^ expected identifier | | | while parsing the fields for this pattern diff --git a/tests/ui/parser/cfg-keyword-lifetime.rs b/tests/ui/parser/cfg-keyword-lifetime.rs index a1588eddc074..c61b69175c88 100644 --- a/tests/ui/parser/cfg-keyword-lifetime.rs +++ b/tests/ui/parser/cfg-keyword-lifetime.rs @@ -1,6 +1,6 @@ // Disallow `'keyword` even in cfg'd code. -#[cfg(any())] +#[cfg(false)] fn hello() -> &'ref () {} //~^ ERROR lifetimes cannot use keyword names diff --git a/tests/ui/parser/issue-116781.rs b/tests/ui/parser/issue-116781.rs index 0e951d2eaa44..176350fe2eec 100644 --- a/tests/ui/parser/issue-116781.rs +++ b/tests/ui/parser/issue-116781.rs @@ -1,6 +1,6 @@ #[derive(Debug)] struct Foo { - #[cfg(all())] + #[cfg(true)] field: fn(($),), //~ ERROR expected pattern, found `$` //~^ ERROR expected pattern, found `$` } diff --git a/tests/ui/parser/raw/raw-idents.rs b/tests/ui/parser/raw/raw-idents.rs index 93015ee6c494..4e1e6b124c31 100644 --- a/tests/ui/parser/raw/raw-idents.rs +++ b/tests/ui/parser/raw/raw-idents.rs @@ -62,7 +62,7 @@ macro_rules! tests { impl<$kw> B<$kw> {} } mod extern_crate { - #[cfg(any())] + #[cfg(false)] extern crate $kw; } mod body { diff --git a/tests/ui/parser/ty-path-followed-by-single-colon.rs b/tests/ui/parser/ty-path-followed-by-single-colon.rs index a9082ea317a7..588fec3f2fc8 100644 --- a/tests/ui/parser/ty-path-followed-by-single-colon.rs +++ b/tests/ui/parser/ty-path-followed-by-single-colon.rs @@ -12,7 +12,7 @@ mod garden { fn g(_: impl Take) {} // OK! - #[cfg(any())] fn h() where a::path:to::nowhere {} // OK! + #[cfg(false)] fn h() where a::path:to::nowhere {} // OK! fn i(_: impl Take:to::somewhere>) {} // OK! diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs.rs b/tests/ui/proc-macro/ambiguous-builtin-attrs.rs index 63d3c79055ca..e36a12beb818 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs.rs +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs.rs @@ -9,7 +9,7 @@ use builtin_attrs::{bench, test}; #[repr(C)] //~ ERROR `repr` is ambiguous struct S; -#[cfg_attr(all(), repr(C))] //~ ERROR `repr` is ambiguous +#[cfg_attr(true, repr(C))] //~ ERROR `repr` is ambiguous struct SCond; #[test] // OK, shadowed diff --git a/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr b/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr index ff7894a41eab..d05701986260 100644 --- a/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr +++ b/tests/ui/proc-macro/ambiguous-builtin-attrs.stderr @@ -20,10 +20,10 @@ LL | use builtin_attrs::*; = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `repr` is ambiguous - --> $DIR/ambiguous-builtin-attrs.rs:12:19 + --> $DIR/ambiguous-builtin-attrs.rs:12:18 | -LL | #[cfg_attr(all(), repr(C))] - | ^^^^ ambiguous name +LL | #[cfg_attr(true, repr(C))] + | ^^^^ ambiguous name | = note: ambiguous because of a name conflict with a builtin attribute = note: `repr` could refer to a built-in attribute diff --git a/tests/ui/proc-macro/auxiliary/derive-attr-cfg.rs b/tests/ui/proc-macro/auxiliary/derive-attr-cfg.rs index cb60c182a43c..b1da93de2a6e 100644 --- a/tests/ui/proc-macro/auxiliary/derive-attr-cfg.rs +++ b/tests/ui/proc-macro/auxiliary/derive-attr-cfg.rs @@ -4,6 +4,6 @@ use proc_macro::TokenStream; #[proc_macro_derive(Foo, attributes(foo))] pub fn derive(input: TokenStream) -> TokenStream { - assert!(!input.to_string().contains("#[cfg(any())]")); + assert!(!input.to_string().contains("#[cfg(false)]")); "".parse().unwrap() } diff --git a/tests/ui/proc-macro/cfg-eval.rs b/tests/ui/proc-macro/cfg-eval.rs index 9e9e46912588..60c94ad2a752 100644 --- a/tests/ui/proc-macro/cfg-eval.rs +++ b/tests/ui/proc-macro/cfg-eval.rs @@ -17,9 +17,9 @@ extern crate test_macros; struct S1 { #[cfg(false)] field_false: u8, - #[cfg(all(/*true*/))] + #[cfg(true)] #[cfg_attr(FALSE, unknown_attr)] - #[cfg_attr(all(/*true*/), allow())] //~ WARN unused attribute + #[cfg_attr(true, allow())] //~ WARN unused attribute field_true: u8, } @@ -29,9 +29,9 @@ struct S2 {} fn main() { // Subtle - we need a trailing comma after the '1' - otherwise, `#[cfg_eval]` will - // turn this into `(#[cfg(all())] 1)`, which is a parenthesized expression, not a tuple + // turn this into `(#[cfg(true)] 1)`, which is a parenthesized expression, not a tuple // expression. `#[cfg]` is not supported inside parenthesized expressions, so this will // produce an error when attribute collection runs. let _ = #[cfg_eval] #[print_attr] #[cfg_attr(not(FALSE), rustc_dummy)] - (#[cfg(false)] 0, #[cfg(all(/*true*/))] 1,); + (#[cfg(false)] 0, #[cfg(true)] 1,); } diff --git a/tests/ui/proc-macro/cfg-eval.stderr b/tests/ui/proc-macro/cfg-eval.stderr index 1429dbde7bfc..72c452c7a08a 100644 --- a/tests/ui/proc-macro/cfg-eval.stderr +++ b/tests/ui/proc-macro/cfg-eval.stderr @@ -1,8 +1,8 @@ warning: unused attribute --> $DIR/cfg-eval.rs:22:5 | -LL | #[cfg_attr(all(/*true*/), allow())] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #[cfg_attr(true, allow())] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: attribute `allow` with an empty list has no effect = note: requested on the command line with `-W unused-attributes` diff --git a/tests/ui/proc-macro/cfg-eval.stdout b/tests/ui/proc-macro/cfg-eval.stdout index 5d88297ad688..6493eee46547 100644 --- a/tests/ui/proc-macro/cfg-eval.stdout +++ b/tests/ui/proc-macro/cfg-eval.stdout @@ -1,5 +1,5 @@ -PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true: u8, } -PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8, } +PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(true)] #[allow()] field_true: u8, } +PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): struct S1 { #[cfg(true)] #[allow()] field_true : u8, } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -28,19 +28,14 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "all", - span: $DIR/cfg-eval.rs:20:11: 20:14 (#0), - }, - Group { - delimiter: Parenthesis, - stream: TokenStream [], - span: $DIR/cfg-eval.rs:20:14: 20:24 (#0), + ident: "true", + span: $DIR/cfg-eval.rs:20:11: 20:15 (#0), }, ], - span: $DIR/cfg-eval.rs:20:10: 20:25 (#0), + span: $DIR/cfg-eval.rs:20:10: 20:16 (#0), }, ], - span: $DIR/cfg-eval.rs:20:6: 20:26 (#0), + span: $DIR/cfg-eval.rs:20:6: 20:17 (#0), }, Punct { ch: '#', @@ -52,15 +47,15 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "allow", - span: $DIR/cfg-eval.rs:22:31: 22:36 (#0), + span: $DIR/cfg-eval.rs:22:22: 22:27 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/cfg-eval.rs:22:36: 22:38 (#0), + span: $DIR/cfg-eval.rs:22:27: 22:29 (#0), }, ], - span: $DIR/cfg-eval.rs:22:6: 22:40 (#0), + span: $DIR/cfg-eval.rs:22:6: 22:31 (#0), }, Ident { ident: "field_true", @@ -84,7 +79,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ span: $DIR/cfg-eval.rs:17:11: 24:2 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] (#[cfg(all())] 1,) +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] (#[cfg(true)] 1,) PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', @@ -120,32 +115,27 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "all", - span: $DIR/cfg-eval.rs:36:29: 36:32 (#0), - }, - Group { - delimiter: Parenthesis, - stream: TokenStream [], - span: $DIR/cfg-eval.rs:36:32: 36:42 (#0), + ident: "true", + span: $DIR/cfg-eval.rs:36:29: 36:33 (#0), }, ], - span: $DIR/cfg-eval.rs:36:28: 36:43 (#0), + span: $DIR/cfg-eval.rs:36:28: 36:34 (#0), }, ], - span: $DIR/cfg-eval.rs:36:24: 36:44 (#0), + span: $DIR/cfg-eval.rs:36:24: 36:35 (#0), }, Literal { kind: Integer, symbol: "1", suffix: None, - span: $DIR/cfg-eval.rs:36:45: 36:46 (#0), + span: $DIR/cfg-eval.rs:36:36: 36:37 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/cfg-eval.rs:36:46: 36:47 (#0), + span: $DIR/cfg-eval.rs:36:37: 36:38 (#0), }, ], - span: $DIR/cfg-eval.rs:36:5: 36:48 (#0), + span: $DIR/cfg-eval.rs:36:5: 36:39 (#0), }, ] diff --git a/tests/ui/proc-macro/derive-attr-cfg.rs b/tests/ui/proc-macro/derive-attr-cfg.rs index 2f3516cabae9..21d3a93ffa6c 100644 --- a/tests/ui/proc-macro/derive-attr-cfg.rs +++ b/tests/ui/proc-macro/derive-attr-cfg.rs @@ -9,7 +9,7 @@ use derive_attr_cfg::Foo; #[derive(Foo)] #[foo] struct S { - #[cfg(any())] + #[cfg(false)] x: i32 } diff --git a/tests/ui/proc-macro/derive-b.rs b/tests/ui/proc-macro/derive-b.rs index 68d341478f18..c04152f629ca 100644 --- a/tests/ui/proc-macro/derive-b.rs +++ b/tests/ui/proc-macro/derive-b.rs @@ -4,7 +4,7 @@ extern crate derive_b_rpass as derive_b; #[derive(Debug, PartialEq, derive_b::B, Eq, Copy, Clone)] -#[cfg_attr(all(), B[arbitrary tokens])] +#[cfg_attr(true, B[arbitrary tokens])] struct B { #[C] a: u64 diff --git a/tests/ui/proc-macro/derive-helper-configured.rs b/tests/ui/proc-macro/derive-helper-configured.rs index b753e29b8bf3..b96ebdebaebb 100644 --- a/tests/ui/proc-macro/derive-helper-configured.rs +++ b/tests/ui/proc-macro/derive-helper-configured.rs @@ -7,9 +7,9 @@ extern crate test_macros; #[derive(Empty)] -#[cfg_attr(all(), empty_helper)] +#[cfg_attr(true, empty_helper)] struct S { - #[cfg_attr(all(), empty_helper)] + #[cfg_attr(true, empty_helper)] field: u8, } diff --git a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed index cfbf9ef38684..f6fbff175a99 100644 --- a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed +++ b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed @@ -3,7 +3,7 @@ //@ compile-flags: -Aunused use y::z; -#[cfg(all())] +#[cfg(true)] use y::Whatever; mod y { diff --git a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs index 98be104d8fde..73f263e19aa4 100644 --- a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs +++ b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs @@ -2,7 +2,7 @@ //@ run-rustfix //@ compile-flags: -Aunused -#[cfg(all())] +#[cfg(true)] use y::Whatever; mod y { diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed index a2f04daa4b85..c1adc90161a4 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed @@ -43,7 +43,7 @@ macro_rules! meta2 { macro_rules! with_cfg_attr { () => { - #[cfg_attr(all(), unsafe(link_section = ".custom_section"))] + #[cfg_attr(true, unsafe(link_section = ".custom_section"))] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition pub extern "C" fn abc() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs index 88c9328fd9cd..9fdf37904634 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs @@ -43,7 +43,7 @@ macro_rules! meta2 { macro_rules! with_cfg_attr { () => { - #[cfg_attr(all(), link_section = ".custom_section")] + #[cfg_attr(true, link_section = ".custom_section")] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition pub extern "C" fn abc() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr index 55df60c51d9c..279e61a9cb67 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr @@ -77,10 +77,10 @@ LL | #[unsafe($e = $l)] | +++++++ + error: unsafe attribute used without unsafe - --> $DIR/unsafe-attributes-fix.rs:46:27 + --> $DIR/unsafe-attributes-fix.rs:46:26 | -LL | #[cfg_attr(all(), link_section = ".custom_section")] - | ^^^^^^^^^^^^ usage of unsafe attribute +LL | #[cfg_attr(true, link_section = ".custom_section")] + | ^^^^^^^^^^^^ usage of unsafe attribute ... LL | with_cfg_attr!(); | ---------------- in this macro invocation @@ -90,8 +90,8 @@ LL | with_cfg_attr!(); = note: this error originates in the macro `with_cfg_attr` (in Nightly builds, run with -Z macro-backtrace for more info) help: wrap the attribute in `unsafe(...)` | -LL | #[cfg_attr(all(), unsafe(link_section = ".custom_section"))] - | +++++++ + +LL | #[cfg_attr(true, unsafe(link_section = ".custom_section"))] + | +++++++ + error: unsafe attribute used without unsafe --> $DIR/unsafe-attributes-fix.rs:67:3 diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs index e2db7c01adf2..c1c9f79996ed 100644 --- a/tests/ui/static/static-align.rs +++ b/tests/ui/static/static-align.rs @@ -53,7 +53,7 @@ thread_local! { #[allow(unused_mut, reason = "test attribute handling")] #[cfg_attr(TRUE, cfg_attr(FOURTY_TWO = "42", - cfg_attr(all(), + cfg_attr(true, cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096))))))] #[allow(unused_mut, reason = "test attribute handling")] diff --git a/tests/ui/structs/struct-field-cfg.rs b/tests/ui/structs/struct-field-cfg.rs index 42cab8ab916b..84f913927dab 100644 --- a/tests/ui/structs/struct-field-cfg.rs +++ b/tests/ui/structs/struct-field-cfg.rs @@ -3,16 +3,16 @@ struct Foo { } fn main() { - let foo = Foo { #[cfg(all())] present: () }; - let _ = Foo { #[cfg(any())] present: () }; + let foo = Foo { #[cfg(true)] present: () }; + let _ = Foo { #[cfg(false)] present: () }; //~^ ERROR missing field `present` in initializer of `Foo` - let _ = Foo { present: (), #[cfg(any())] absent: () }; - let _ = Foo { present: (), #[cfg(all())] absent: () }; + let _ = Foo { present: (), #[cfg(false)] absent: () }; + let _ = Foo { present: (), #[cfg(true)] absent: () }; //~^ ERROR struct `Foo` has no field named `absent` - let Foo { #[cfg(all())] present: () } = foo; - let Foo { #[cfg(any())] present: () } = foo; + let Foo { #[cfg(true)] present: () } = foo; + let Foo { #[cfg(false)] present: () } = foo; //~^ ERROR pattern does not mention field `present` - let Foo { present: (), #[cfg(any())] absent: () } = foo; - let Foo { present: (), #[cfg(all())] absent: () } = foo; + let Foo { present: (), #[cfg(false)] absent: () } = foo; + let Foo { present: (), #[cfg(true)] absent: () } = foo; //~^ ERROR struct `Foo` does not have a field named `absent` } diff --git a/tests/ui/structs/struct-field-cfg.stderr b/tests/ui/structs/struct-field-cfg.stderr index 2bca6f302db7..db280a632d63 100644 --- a/tests/ui/structs/struct-field-cfg.stderr +++ b/tests/ui/structs/struct-field-cfg.stderr @@ -1,44 +1,44 @@ error[E0063]: missing field `present` in initializer of `Foo` --> $DIR/struct-field-cfg.rs:7:13 | -LL | let _ = Foo { #[cfg(any())] present: () }; +LL | let _ = Foo { #[cfg(false)] present: () }; | ^^^ missing `present` error[E0560]: struct `Foo` has no field named `absent` - --> $DIR/struct-field-cfg.rs:10:46 + --> $DIR/struct-field-cfg.rs:10:45 | -LL | let _ = Foo { present: (), #[cfg(all())] absent: () }; - | ^^^^^^ `Foo` does not have this field +LL | let _ = Foo { present: (), #[cfg(true)] absent: () }; + | ^^^^^^ `Foo` does not have this field | = note: all struct fields are already assigned error[E0027]: pattern does not mention field `present` --> $DIR/struct-field-cfg.rs:13:9 | -LL | let Foo { #[cfg(any())] present: () } = foo; +LL | let Foo { #[cfg(false)] present: () } = foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `present` | help: include the missing field in the pattern | -LL - let Foo { #[cfg(any())] present: () } = foo; +LL - let Foo { #[cfg(false)] present: () } = foo; LL + let Foo { present } = foo; | help: if you don't care about this missing field, you can explicitly ignore it | -LL - let Foo { #[cfg(any())] present: () } = foo; +LL - let Foo { #[cfg(false)] present: () } = foo; LL + let Foo { present: _ } = foo; | help: or always ignore missing fields here | -LL - let Foo { #[cfg(any())] present: () } = foo; +LL - let Foo { #[cfg(false)] present: () } = foo; LL + let Foo { .. } = foo; | error[E0026]: struct `Foo` does not have a field named `absent` - --> $DIR/struct-field-cfg.rs:16:42 + --> $DIR/struct-field-cfg.rs:16:41 | -LL | let Foo { present: (), #[cfg(all())] absent: () } = foo; - | ^^^^^^ struct `Foo` does not have this field +LL | let Foo { present: (), #[cfg(true)] absent: () } = foo; + | ^^^^^^ struct `Foo` does not have this field error: aborting due to 4 previous errors diff --git a/tests/ui/test-attrs/issue-34932.rs b/tests/ui/test-attrs/issue-34932.rs index feb6556b60a1..f0cbdd07df84 100644 --- a/tests/ui/test-attrs/issue-34932.rs +++ b/tests/ui/test-attrs/issue-34932.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags:--test -#![cfg(any())] // This test should be configured away +#![cfg(false)] // This test should be configured away #![feature(rustc_attrs)] // Test that this is allowed on stable/beta #![feature(iter_arith_traits)] // Test that this is not unused #![deny(unused_features)] diff --git a/tests/ui/typeck/issue-86721-return-expr-ice.rs b/tests/ui/typeck/issue-86721-return-expr-ice.rs index ea3a2f2fbfe6..ed36164aea60 100644 --- a/tests/ui/typeck/issue-86721-return-expr-ice.rs +++ b/tests/ui/typeck/issue-86721-return-expr-ice.rs @@ -1,7 +1,7 @@ // Regression test for the ICE described in #86721. //@ revisions: rev1 rev2 -#![cfg_attr(any(), rev1, rev2)] +#![cfg_attr(false, rev1, rev2)] #![crate_type = "lib"] #[cfg(any(rev1))] From a96d486cbd96c5f6819bec25e1f23a71b23233a7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 11 Dec 2025 14:01:00 +0100 Subject: [PATCH 45/56] Update snapshot --- src/bootstrap/src/core/builder/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b8ba1b4c2c34..28659f4c6fa5 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2912,6 +2912,7 @@ mod snapshot { [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc + [dist] rustc 1 -> rustc-dev 2 [build] rustc 1 -> cargo 2 [dist] rustc 1 -> cargo 2 [build] rustc 1 -> rust-analyzer 2 From 42c8b68ee9b591b79cbdfd68f5656a172815aa21 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 11 Dec 2025 18:48:10 +0100 Subject: [PATCH 46/56] Bump nightly version -> 2025-12-11 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 4b1a10a3d9cf..dc8695fef9f5 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-11-28 +nightly-2025-12-11 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5157b79832a3..1384f4078ebe 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-11-28" +channel = "nightly-2025-12-11" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 1ff46825908de84aa401b5be0ef7b70a89d8e8da Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 11 Dec 2025 18:48:15 +0100 Subject: [PATCH 47/56] Bump Clippy version -> 0.1.94 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fee885d8fa7e..67078adea2b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.93" +version = "0.1.94" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 3f6b26d3334e..a65fe7bcbda5 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.93" +version = "0.1.94" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index bc97746a1cba..7a78ef32bf3c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.93" +version = "0.1.94" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index f2e276b27a98..503d581d6c7f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.93" +version = "0.1.94" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index b73a7c7bb4d9..ee6d6cdbc34e 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.93" +version = "0.1.94" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" From 43fa2a95c90d7ac4bb98d87e62630e9f549d00c2 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 11 Dec 2025 18:15:15 +0000 Subject: [PATCH 48/56] add regression test for `proc_macro` error subdiagnostics --- tests/ui/proc-macro/auxiliary/sub-error-diag.rs | 17 +++++++++++++++++ tests/ui/proc-macro/sub-error-diag.rs | 13 +++++++++++++ tests/ui/proc-macro/sub-error-diag.stderr | 11 +++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/sub-error-diag.rs create mode 100644 tests/ui/proc-macro/sub-error-diag.rs create mode 100644 tests/ui/proc-macro/sub-error-diag.stderr diff --git a/tests/ui/proc-macro/auxiliary/sub-error-diag.rs b/tests/ui/proc-macro/auxiliary/sub-error-diag.rs new file mode 100644 index 000000000000..5ce8c5d90304 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/sub-error-diag.rs @@ -0,0 +1,17 @@ +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use proc_macro::{Diagnostic, Level, Span}; + +#[proc_macro_attribute] +pub fn proc_emit_err( + _: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + Diagnostic::new(Level::Error, "Parent message") + .span_error(Span::call_site(), "Child message") + .emit(); + + input +} diff --git a/tests/ui/proc-macro/sub-error-diag.rs b/tests/ui/proc-macro/sub-error-diag.rs new file mode 100644 index 000000000000..11218fc66a37 --- /dev/null +++ b/tests/ui/proc-macro/sub-error-diag.rs @@ -0,0 +1,13 @@ +//@ proc-macro: sub-error-diag.rs + +// Regression test for issue https://github.com/rust-lang/rust/issues/145305, which used to cause an ICE +// due to an assertion in the compiler that errors could not be subdiagnostics. + +extern crate sub_error_diag; + +//~? ERROR: Parent message +#[sub_error_diag::proc_emit_err] +//~^ ERROR: Child message +fn foo() {} + +fn main() {} diff --git a/tests/ui/proc-macro/sub-error-diag.stderr b/tests/ui/proc-macro/sub-error-diag.stderr new file mode 100644 index 000000000000..b5d83e4d52ae --- /dev/null +++ b/tests/ui/proc-macro/sub-error-diag.stderr @@ -0,0 +1,11 @@ +error: Parent message + | +error: Child message + --> $DIR/sub-error-diag.rs:9:1 + | +LL | #[sub_error_diag::proc_emit_err] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `sub_error_diag::proc_emit_err` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + From 26c164f60cf6491146c5000cfda5c6602ac22088 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 11 Dec 2025 19:16:53 +0100 Subject: [PATCH 49/56] Update Cargo.lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03a4d71f67c3..0be6de0a2871 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,7 +621,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clippy" -version = "0.1.93" +version = "0.1.94" dependencies = [ "anstream", "askama", @@ -648,7 +648,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.93" +version = "0.1.94" dependencies = [ "clippy_utils", "itertools", @@ -671,7 +671,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.93" +version = "0.1.94" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -703,7 +703,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.93" +version = "0.1.94" dependencies = [ "arrayvec", "itertools", @@ -1107,7 +1107,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.93" +version = "0.1.94" [[package]] name = "derive-where" From 03493597922f6d1527955b1d7558dd369126f1d9 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Mon, 8 Dec 2025 21:39:59 +0000 Subject: [PATCH 50/56] Suggest `cfg(false)` instead of `cfg(FALSE)` --- compiler/rustc_lint/messages.ftl | 1 + .../src/early/diagnostics/check_cfg.rs | 25 +++++ compiler/rustc_lint/src/lints.rs | 11 +++ tests/ui/check-cfg/false.rs | 61 ++++++++++++ tests/ui/check-cfg/false.stderr | 95 +++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100644 tests/ui/check-cfg/false.rs create mode 100644 tests/ui/check-cfg/false.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index bc1c246b8f03..846c83aff2d3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -829,6 +829,7 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` +lint_unexpected_cfg_boolean = you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}` lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index e2f5dd315d57..0c8d7523a9dc 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -138,6 +138,16 @@ pub(super) fn unexpected_cfg_name( let is_from_external_macro = name_span.in_external_macro(sess.source_map()); let mut is_feature_cfg = name == sym::feature; + fn miscapitalized_boolean(name: Symbol) -> Option { + if name.as_str().eq_ignore_ascii_case("false") { + Some(false) + } else if name.as_str().eq_ignore_ascii_case("true") { + Some(true) + } else { + None + } + } + let code_sugg = if is_feature_cfg && is_from_cargo { lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures // Suggest correct `version("..")` predicate syntax @@ -148,6 +158,21 @@ pub(super) fn unexpected_cfg_name( between_name_and_value: name_span.between(value_span), after_value: value_span.shrink_to_hi(), } + // Suggest a literal `false` instead + // Detect miscapitalized `False`/`FALSE` etc, ensuring that this isn't `r#false` + } else if value.is_none() + // If this is a miscapitalized False/FALSE, suggest the boolean literal instead + && let Some(boolean) = miscapitalized_boolean(name) + // Check this isn't a raw identifier + && sess + .source_map() + .span_to_snippet(name_span) + .map_or(true, |snippet| !snippet.contains("r#")) + { + lints::unexpected_cfg_name::CodeSuggestion::BooleanLiteral { + span: name_span, + literal: boolean, + } // Suggest the most probable if we found one } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { is_feature_cfg |= best_match == sym::feature; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1bec316ce45a..3f853e09c72d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2401,6 +2401,17 @@ pub(crate) mod unexpected_cfg_name { #[subdiagnostic] expected_names: Option, }, + #[suggestion( + lint_unexpected_cfg_boolean, + applicability = "machine-applicable", + style = "verbose", + code = "{literal}" + )] + BooleanLiteral { + #[primary_span] + span: Span, + literal: bool, + }, } #[derive(Subdiagnostic)] diff --git a/tests/ui/check-cfg/false.rs b/tests/ui/check-cfg/false.rs new file mode 100644 index 000000000000..f7ed43ccfa95 --- /dev/null +++ b/tests/ui/check-cfg/false.rs @@ -0,0 +1,61 @@ +//! Check that `cfg(false)` is suggested instead of cfg(FALSE) +// +//@ check-pass +//@ no-auto-check-cfg +//@ compile-flags: --check-cfg=cfg() + +#[cfg(FALSE)] +//~^ WARNING unexpected `cfg` condition name: `FALSE` +//~| HELP: to expect this configuration use +//~| HELP: you may have meant to use `false` (notice the capitalization). +pub fn a() {} + +#[cfg(False)] +//~^ WARNING unexpected `cfg` condition name: `False` +//~| HELP: to expect this configuration use +//~| HELP: you may have meant to use `false` (notice the capitalization). +pub fn b() {} + +#[cfg(r#false)] +//~^ WARNING unexpected `cfg` condition name: `r#false` +//~| HELP: to expect this configuration use +// No capitalization help for r#false +pub fn c() {} + +#[cfg(r#False)] +//~^ WARNING unexpected `cfg` condition name: `False` +//~| HELP: to expect this configuration use +// No capitalization help for r#False +pub fn d() {} + +#[cfg(false)] +pub fn e() {} + +#[cfg(TRUE)] +//~^ WARNING unexpected `cfg` condition name: `TRUE` +//~| HELP: to expect this configuration use +//~| HELP: you may have meant to use `true` (notice the capitalization). +pub fn f() {} + +#[cfg(True)] +//~^ WARNING unexpected `cfg` condition name: `True` +//~| HELP: to expect this configuration use +//~| HELP: you may have meant to use `true` (notice the capitalization). +pub fn g() {} + +#[cfg(r#true)] +//~^ WARNING unexpected `cfg` condition name: `r#true` +//~| HELP: to expect this configuration use +// No capitalization help for r#true +pub fn h() {} + +#[cfg(r#True)] +//~^ WARNING unexpected `cfg` condition name: `True` +//~| HELP: to expect this configuration use +// No capitalization help for r#True +pub fn i() {} + +#[cfg(true)] +pub fn j() {} + +pub fn main() {} diff --git a/tests/ui/check-cfg/false.stderr b/tests/ui/check-cfg/false.stderr new file mode 100644 index 000000000000..f4480a3dc67e --- /dev/null +++ b/tests/ui/check-cfg/false.stderr @@ -0,0 +1,95 @@ +warning: unexpected `cfg` condition name: `FALSE` + --> $DIR/false.rs:7:7 + | +LL | #[cfg(FALSE)] + | ^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(FALSE)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +help: you may have meant to use `false` (notice the capitalization). Doing so makes this predicate evaluate to `false` unconditionally + | +LL - #[cfg(FALSE)] +LL + #[cfg(false)] + | + +warning: unexpected `cfg` condition name: `False` + --> $DIR/false.rs:13:7 + | +LL | #[cfg(False)] + | ^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(False)` + = note: see for more information about checking conditional configuration +help: you may have meant to use `false` (notice the capitalization). Doing so makes this predicate evaluate to `false` unconditionally (notice the capitalization) + | +LL - #[cfg(False)] +LL + #[cfg(false)] + | + +warning: unexpected `cfg` condition name: `r#false` + --> $DIR/false.rs:19:7 + | +LL | #[cfg(r#false)] + | ^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(r#false)` + = note: see for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `False` + --> $DIR/false.rs:25:7 + | +LL | #[cfg(r#False)] + | ^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(False)` + = note: see for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `TRUE` + --> $DIR/false.rs:34:7 + | +LL | #[cfg(TRUE)] + | ^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(TRUE)` + = note: see for more information about checking conditional configuration +help: you may have meant to use `true` (notice the capitalization). Doing so makes this predicate evaluate to `true` unconditionally + | +LL - #[cfg(TRUE)] +LL + #[cfg(true)] + | + +warning: unexpected `cfg` condition name: `True` + --> $DIR/false.rs:40:7 + | +LL | #[cfg(True)] + | ^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(True)` + = note: see for more information about checking conditional configuration +help: you may have meant to use `true` (notice the capitalization). Doing so makes this predicate evaluate to `true` unconditionally + | +LL - #[cfg(True)] +LL + #[cfg(true)] + | + +warning: unexpected `cfg` condition name: `r#true` + --> $DIR/false.rs:46:7 + | +LL | #[cfg(r#true)] + | ^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(r#true)` + = note: see for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `True` + --> $DIR/false.rs:52:7 + | +LL | #[cfg(r#True)] + | ^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(True)` + = note: see for more information about checking conditional configuration + +warning: 8 warnings emitted + From de0d961eb3cf26c73b2d03464845ab465a5484e5 Mon Sep 17 00:00:00 2001 From: Makai Date: Fri, 12 Dec 2025 10:25:23 +0800 Subject: [PATCH 51/56] triagebot: ping makai410 for rustc_public --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 397e6875eef9..00e2d4c787ab 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1154,7 +1154,7 @@ cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"] [mentions."compiler/rustc_public"] message = "This PR changes rustc_public" -cc = ["@oli-obk", "@celinval", "@ouz-a"] +cc = ["@oli-obk", "@celinval", "@ouz-a", "@makai410"] [mentions."compiler/rustc_target/src/spec"] message = """ From 24302fd95f026197304b4814cab668553b4b345c Mon Sep 17 00:00:00 2001 From: Makai Date: Fri, 12 Dec 2025 10:26:03 +0800 Subject: [PATCH 52/56] triagebot: add myself(makai410) to the review rotation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 00e2d4c787ab..3182122bdeb0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1542,6 +1542,7 @@ project-stable-mir = [ "@celinval", "@oli-obk", "@scottmcm", + "@makai410", ] project-exploit-mitigations = [ "@cuviper", From 5aa2c14aebc731f54e737231e6f57cc7fc47a659 Mon Sep 17 00:00:00 2001 From: Makai Date: Fri, 12 Dec 2025 10:35:13 +0800 Subject: [PATCH 53/56] mailmap: add makai410 --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index a586e7047769..4c254b396b53 100644 --- a/.mailmap +++ b/.mailmap @@ -431,6 +431,7 @@ Lzu Tao Maik Klein Maja Kądziołka Maja Kądziołka +Makai Malo Jaffré Manish Goregaokar Mara Bos From 97252d3747161635253ec7fefa413c2843be5321 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 8 Dec 2025 22:31:55 +0100 Subject: [PATCH 54/56] Remove from `MetaItemParser::from_attr` --- compiler/rustc_attr_parsing/src/interface.rs | 21 ++++++++++---------- compiler/rustc_attr_parsing/src/parser.rs | 18 +---------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 5eefce75ace2..45b46803419d 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::convert::identity; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; @@ -13,7 +14,7 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; -use crate::parser::{ArgParser, MetaItemParser, PathParser}; +use crate::parser::{ArgParser, PathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; @@ -144,22 +145,23 @@ impl<'sess> AttributeParser<'sess, Early> { }; let parts = normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); - let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?; - let path = meta_parser.path(); - let args = meta_parser.args(); + + let path = AttrPath::from_ast(&normal_attr.item.path, identity); + let args = + ArgParser::from_attr_args(&normal_attr.item.args, &parts, &sess.psess, emit_errors)?; Self::parse_single_args( sess, attr.span, normal_attr.item.span(), attr.style, - path.get_attribute_path(), + path, Some(normal_attr.item.unsafety), ParsedDescription::Attribute, target_span, target_node_id, features, emit_errors, - args, + &args, parse_fn, template, ) @@ -316,15 +318,14 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { - let Some(parser) = MetaItemParser::from_attr( - n, + let Some(args) = ArgParser::from_attr_args( + &n.item.args, &parts, &self.sess.psess, self.stage.should_emit(), ) else { continue; }; - let args = parser.args(); // Special-case handling for `#[doc = "..."]`: if we go through with // `DocParser`, the order of doc comments will be messed up because `///` @@ -373,7 +374,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { attr_path: attr_path.clone(), }; - (accept.accept_fn)(&mut cx, args); + (accept.accept_fn)(&mut cx, &args); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { Self::check_target(&accept.allowed_targets, target, &mut cx); } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 819e5630561d..2304fb0ca63c 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -8,7 +8,7 @@ use std::fmt::{Debug, Display}; use rustc_ast::token::{self, Delimiter, MetaVarKind}; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path, StmtKind, UnOp}; +use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, StmtKind, UnOp}; use rustc_ast_pretty::pprust; use rustc_errors::{Diag, PResult}; use rustc_hir::{self as hir, AttrPath}; @@ -252,22 +252,6 @@ impl<'a> Debug for MetaItemParser<'a> { } } -impl<'a> MetaItemParser<'a> { - /// Create a new parser from a [`NormalAttr`], which is stored inside of any - /// [`ast::Attribute`](rustc_ast::Attribute) - pub fn from_attr<'sess>( - attr: &'a NormalAttr, - parts: &[Symbol], - psess: &'sess ParseSess, - should_emit: ShouldEmit, - ) -> Option { - Some(Self { - path: PathParser(Cow::Borrowed(&attr.item.path)), - args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?, - }) - } -} - impl<'a> MetaItemParser<'a> { pub fn span(&self) -> Span { if let Some(other) = self.args.span() { From 86a97c41cbfae596cad6e6e9191c414e7a77a520 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 8 Dec 2025 22:48:13 +0100 Subject: [PATCH 55/56] Remove lifetime param from parser types --- .../src/attributes/allow_unstable.rs | 8 +- .../rustc_attr_parsing/src/attributes/cfg.rs | 8 +- .../src/attributes/codegen_attrs.rs | 18 ++-- .../src/attributes/crate_level.rs | 12 +-- .../src/attributes/debugger.rs | 2 +- .../src/attributes/deprecation.rs | 4 +- .../src/attributes/dummy.rs | 2 +- .../src/attributes/inline.rs | 4 +- .../src/attributes/link_attrs.rs | 22 ++--- .../src/attributes/macro_attrs.rs | 2 +- .../rustc_attr_parsing/src/attributes/mod.rs | 8 +- .../src/attributes/must_use.rs | 2 +- .../rustc_attr_parsing/src/attributes/path.rs | 2 +- .../src/attributes/proc_macro_attrs.rs | 6 +- .../src/attributes/prototype.rs | 4 +- .../rustc_attr_parsing/src/attributes/repr.rs | 21 ++--- .../src/attributes/rustc_internal.rs | 8 +- .../src/attributes/stability.rs | 6 +- .../src/attributes/test_attrs.rs | 4 +- .../src/attributes/traits.rs | 2 +- .../src/attributes/transparency.rs | 2 +- .../rustc_attr_parsing/src/attributes/util.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 6 +- compiler/rustc_attr_parsing/src/interface.rs | 9 +- compiler/rustc_attr_parsing/src/parser.rs | 83 ++++++++++--------- 25 files changed, 119 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 088fa73d7427..faa366a62831 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -19,7 +19,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator { parse_unstable(cx, args, >::PATH[0]) .into_iter() @@ -41,7 +41,7 @@ impl CombineAttributeParser for UnstableFeatureBoundParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator { if !cx.features().staged_api() { cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span }); @@ -69,7 +69,7 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { parse_unstable(cx, args, >::PATH[0]) } @@ -77,7 +77,7 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { fn parse_unstable( cx: &AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, + args: &ArgParser, symbol: Symbol, ) -> impl IntoIterator { let mut res = Vec::new(); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 6ffe25098308..61c314b41b65 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -37,7 +37,7 @@ const CFG_ATTR_TEMPLATE: AttributeTemplate = template!( pub fn parse_cfg<'c, S: Stage>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> Option { let ArgParser::List(list) = args else { cx.expected_list(cx.attr_span); @@ -52,7 +52,7 @@ pub fn parse_cfg<'c, S: Stage>( pub fn parse_cfg_entry( cx: &mut AcceptContext<'_, '_, S>, - item: &MetaItemOrLitParser<'_>, + item: &MetaItemOrLitParser, ) -> Result { Ok(match item { MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() { @@ -98,7 +98,7 @@ pub fn parse_cfg_entry( fn parse_cfg_entry_version( cx: &mut AcceptContext<'_, '_, S>, - list: &MetaItemListParser<'_>, + list: &MetaItemListParser, meta_span: Span, ) -> Result { try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option()); @@ -130,7 +130,7 @@ fn parse_cfg_entry_version( fn parse_cfg_entry_target( cx: &mut AcceptContext<'_, '_, S>, - list: &MetaItemListParser<'_>, + list: &MetaItemListParser, meta_span: Span, ) -> Result { if let Some(features) = cx.features_option() diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index b4ecbe6e4de6..ce208203ec5e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -23,7 +23,7 @@ impl SingleAttributeParser for OptimizeParser { ]); const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { cx.expected_list(cx.attr_span); return None; @@ -84,7 +84,7 @@ impl SingleAttributeParser for CoverageParser { ]); const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(args) = args.list() else { cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]); return None; @@ -135,7 +135,7 @@ impl SingleAttributeParser for ExportNameParser { ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; @@ -164,7 +164,7 @@ impl SingleAttributeParser for ObjcClassParser { AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; @@ -196,7 +196,7 @@ impl SingleAttributeParser for ObjcSelectorParser { AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; @@ -474,7 +474,7 @@ impl AttributeParser for UsedParser { fn parse_tf_attribute<'c, S: Stage>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { let mut features = Vec::new(); let ArgParser::List(list) = args else { @@ -531,7 +531,7 @@ impl CombineAttributeParser for TargetFeatureParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { parse_tf_attribute(cx, args) } @@ -569,7 +569,7 @@ impl CombineAttributeParser for ForceTargetFeatureParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { parse_tf_attribute(cx, args) } @@ -599,7 +599,7 @@ impl SingleAttributeParser for SanitizeParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { cx.expected_list(cx.attr_span); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 480a32658bc5..01c503357fc7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -11,7 +11,7 @@ impl SingleAttributeParser for CrateNameParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(n) = args else { cx.expected_name_value(cx.attr_span, None); return None; @@ -35,7 +35,7 @@ impl SingleAttributeParser for RecursionLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { cx.expected_name_value(cx.attr_span, None); return None; @@ -58,7 +58,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { cx.expected_name_value(cx.attr_span, None); return None; @@ -81,7 +81,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { cx.expected_name_value(cx.attr_span, None); return None; @@ -104,7 +104,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { cx.expected_name_value(cx.attr_span, None); return None; @@ -154,7 +154,7 @@ impl SingleAttributeParser for WindowsSubsystemParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value( args.span().unwrap_or(cx.inner_span), diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index 56ff10be4264..dfb3b914e189 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -18,7 +18,7 @@ impl CombineAttributeParser for DebuggerViualizerParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { let Some(l) = args.list() else { cx.expected_list(args.span().unwrap_or(cx.attr_span)); diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index f96477e28cd0..ad3e2ced60c7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -12,7 +12,7 @@ fn get( cx: &AcceptContext<'_, '_, S>, name: Symbol, param_span: Span, - arg: &ArgParser<'_>, + arg: &ArgParser, item: &Option, ) -> Option { if item.is_some() { @@ -68,7 +68,7 @@ impl SingleAttributeParser for DeprecationParser { NameValueStr: "reason" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let features = cx.features(); let mut since = None; diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs index 7293cee842c2..0a7d95f31799 100644 --- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -15,7 +15,7 @@ impl SingleAttributeParser for DummyParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really - fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option { + fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option { Some(AttributeKind::Dummy) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index fba1a663c057..f6aab9ea0ee2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -34,7 +34,7 @@ impl SingleAttributeParser for InlineParser { "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { match args { ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)), ArgParser::List(list) => { @@ -77,7 +77,7 @@ impl SingleAttributeParser for RustcForceInlineParser { const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let reason = match args { ArgParser::NoArgs => None, ArgParser::List(list) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 46fa8ee71343..67471e31b105 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -33,7 +33,7 @@ impl SingleAttributeParser for LinkNameParser { "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; @@ -64,7 +64,7 @@ impl CombineAttributeParser for LinkParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { let items = match args { ArgParser::List(list) => list, @@ -242,7 +242,7 @@ impl CombineAttributeParser for LinkParser { impl LinkParser { fn parse_link_name( - item: &MetaItemParser<'_>, + item: &MetaItemParser, name: &mut Option<(Symbol, Span)>, cx: &mut AcceptContext<'_, '_, S>, ) -> bool { @@ -267,7 +267,7 @@ impl LinkParser { } fn parse_link_kind( - item: &MetaItemParser<'_>, + item: &MetaItemParser, kind: &mut Option, cx: &mut AcceptContext<'_, '_, S>, sess: &Session, @@ -347,7 +347,7 @@ impl LinkParser { } fn parse_link_modifiers( - item: &MetaItemParser<'_>, + item: &MetaItemParser, modifiers: &mut Option<(Symbol, Span)>, cx: &mut AcceptContext<'_, '_, S>, ) -> bool { @@ -368,7 +368,7 @@ impl LinkParser { } fn parse_link_cfg( - item: &MetaItemParser<'_>, + item: &MetaItemParser, cfg: &mut Option, cx: &mut AcceptContext<'_, '_, S>, sess: &Session, @@ -400,7 +400,7 @@ impl LinkParser { } fn parse_link_wasm_import_module( - item: &MetaItemParser<'_>, + item: &MetaItemParser, wasm_import_module: &mut Option<(Symbol, Span)>, cx: &mut AcceptContext<'_, '_, S>, ) -> bool { @@ -421,7 +421,7 @@ impl LinkParser { } fn parse_link_import_name_type( - item: &MetaItemParser<'_>, + item: &MetaItemParser, import_name_type: &mut Option<(PeImportNameType, Span)>, cx: &mut AcceptContext<'_, '_, S>, ) -> bool { @@ -478,7 +478,7 @@ impl SingleAttributeParser for LinkSectionParser { "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; @@ -551,7 +551,7 @@ impl SingleAttributeParser for LinkOrdinalParser { "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ordinal = parse_single_integer(cx, args)?; // According to the table at @@ -607,7 +607,7 @@ impl SingleAttributeParser for LinkageParser { "weak_odr", ]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(name_value) = args.name_value() else { cx.expected_name_value(cx.attr_span, Some(sym::linkage)); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index d2fa1d440f40..e4209c3edd85 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -148,7 +148,7 @@ impl SingleAttributeParser for MacroExportParser { Error(Target::Crate), ]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let local_inner_macros = match args { ArgParser::NoArgs => false, ArgParser::List(list) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 64bcb02b0b74..a26159cb09e7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -62,7 +62,7 @@ pub(crate) mod traits; pub(crate) mod transparency; pub(crate) mod util; -type AcceptFn = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>); +type AcceptFn = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser); type AcceptMapping = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. @@ -133,7 +133,7 @@ pub(crate) trait SingleAttributeParser: 'static { const TEMPLATE: AttributeTemplate; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option; + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option; } /// Use in combination with [`SingleAttributeParser`]. @@ -282,7 +282,7 @@ impl, S: Stage> SingleAttributeParser for Without const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { if let Err(span) = args.no_args() { cx.expected_no_args(span); } @@ -317,7 +317,7 @@ pub(crate) trait CombineAttributeParser: 'static { /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c; } diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index 51b43e96adf9..a27e1ecb707e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -29,7 +29,7 @@ impl SingleAttributeParser for MustUseParser { "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { Some(AttributeKind::MustUse { span: cx.attr_span, reason: match args { diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index e4cb806bb427..b60f8e315e5e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -13,7 +13,7 @@ impl SingleAttributeParser for PathParser { "https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index b9929d6f1f8e..e1762005d4c4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -30,7 +30,7 @@ impl SingleAttributeParser for ProcMacroDeriveParser { "https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?; Some(AttributeKind::ProcMacroDerive { trait_name: trait_name.expect("Trait name is mandatory, so it is present"), @@ -49,7 +49,7 @@ impl SingleAttributeParser for RustcBuiltinMacroParser { const TEMPLATE: AttributeTemplate = template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?; Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span }) } @@ -57,7 +57,7 @@ impl SingleAttributeParser for RustcBuiltinMacroParser { fn parse_derive_like( cx: &mut AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, + args: &ArgParser, trait_name_mandatory: bool, ) -> Option<(Option, ThinVec)> { let Some(list) = args.list() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index 80fe82bf5429..cd7c84f45fe5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -25,7 +25,7 @@ impl SingleAttributeParser for CustomMirParser { const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { cx.expected_list(cx.attr_span); return None; @@ -70,7 +70,7 @@ impl SingleAttributeParser for CustomMirParser { fn extract_value( cx: &mut AcceptContext<'_, '_, S>, key: Symbol, - arg: &ArgParser<'_>, + arg: &ArgParser, span: Span, out_val: &mut Option<(Symbol, Span)>, failed: &mut bool, diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 0330e2515c7d..c7320bf5d96f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -28,7 +28,7 @@ impl CombineAttributeParser for ReprParser { fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + args: &'c ArgParser, ) -> impl IntoIterator + 'c { let mut reprs = Vec::new(); @@ -98,10 +98,7 @@ fn int_type_of_word(s: Symbol) -> Option { } } -fn parse_repr( - cx: &AcceptContext<'_, '_, S>, - param: &MetaItemParser<'_>, -) -> Option { +fn parse_repr(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option { use ReprAttr::*; // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the @@ -192,7 +189,7 @@ enum AlignKind { fn parse_repr_align( cx: &AcceptContext<'_, '_, S>, - list: &MetaItemListParser<'_>, + list: &MetaItemListParser, param_span: Span, align_kind: AlignKind, ) -> Option { @@ -278,11 +275,7 @@ impl AlignParser { const PATH: &'static [Symbol] = &[sym::rustc_align]; const TEMPLATE: AttributeTemplate = template!(List: &[""]); - fn parse<'c, S: Stage>( - &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, - ) { + fn parse<'c, S: Stage>(&mut self, cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser) { match args { ArgParser::NoArgs | ArgParser::NameValue(_) => { cx.expected_list(cx.attr_span); @@ -339,11 +332,7 @@ impl AlignStaticParser { const PATH: &'static [Symbol] = &[sym::rustc_align_static]; const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; - fn parse<'c, S: Stage>( - &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, - ) { + fn parse<'c, S: Stage>(&mut self, cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser) { self.0.parse(cx, args) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 455c61097d78..d51fa2510b93 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -19,7 +19,7 @@ impl SingleAttributeParser for RustcLayoutScalarValidRangeStartPars const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(List: &["start"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { parse_single_integer(cx, args) .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span)) } @@ -34,7 +34,7 @@ impl SingleAttributeParser for RustcLayoutScalarValidRangeEndParser const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(List: &["end"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { parse_single_integer(cx, args) .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span)) } @@ -49,7 +49,7 @@ impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { if let Err(span) = args.no_args() { cx.expected_no_args(span); return None; @@ -68,7 +68,7 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { cx.expected_name_value(cx.attr_span, None); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index b94e23477ffe..b3ebb0f4fec1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -267,7 +267,7 @@ impl AttributeParser for ConstStabilityParser { /// `name = value` fn insert_value_into_option_or_error( cx: &AcceptContext<'_, '_, S>, - param: &MetaItemParser<'_>, + param: &MetaItemParser, item: &mut Option, name: Ident, ) -> Option<()> { @@ -289,7 +289,7 @@ fn insert_value_into_option_or_error( /// its stability information. pub(crate) fn parse_stability( cx: &AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, + args: &ArgParser, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; let mut since = None; @@ -365,7 +365,7 @@ pub(crate) fn parse_stability( /// attribute, and return the feature name and its stability information. pub(crate) fn parse_unstability( cx: &AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, + args: &ArgParser, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; let mut reason = None; diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index e0b006030758..7f25641b948e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -15,7 +15,7 @@ impl SingleAttributeParser for IgnoreParser { "https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { Some(AttributeKind::Ignore { span: cx.attr_span, reason: match args { @@ -49,7 +49,7 @@ impl SingleAttributeParser for ShouldPanicParser { "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute" ); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { Some(AttributeKind::ShouldPanic { span: cx.attr_span, reason: match args { diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 753892d1e997..a9b76021a989 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -18,7 +18,7 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser { const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let mut array = false; let mut boxed_slice = false; let Some(args) = args.list() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ea1f5549c4ec..52aa42b1085a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -17,7 +17,7 @@ impl SingleAttributeParser for TransparencyParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["transparent", "semitransparent", "opaque"]); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { cx.expected_name_value(cx.attr_span, None); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 105f7164bf3b..4e3478abbf4f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -40,7 +40,7 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { /// `args` is the parser for the attribute arguments. pub(crate) fn parse_single_integer( cx: &mut AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, + args: &ArgParser, ) -> Option { let Some(list) = args.list() else { cx.expected_list(cx.attr_span); diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f41ea3708788..074f3b4194ae 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -75,7 +75,7 @@ use crate::attributes::traits::{ }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; -use crate::parser::{ArgParser, PathParser}; +use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem, }; @@ -95,7 +95,7 @@ pub(super) struct GroupTypeInnerAccept { } type AcceptFn = - Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>; + Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>; type FinalizeFn = Box) -> Option>; @@ -713,7 +713,7 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { /// /// Usually, you should use normal attribute parsing logic instead, /// especially when making a *denylist* of other attributes. - pub(crate) all_attrs: &'p [PathParser<'p>], + pub(crate) all_attrs: &'p [RefPathParser<'p>], } impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 45b46803419d..732f8d6e3a2a 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::convert::identity; use rustc_ast as ast; @@ -14,7 +13,7 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; -use crate::parser::{ArgParser, PathParser}; +use crate::parser::{ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; @@ -137,7 +136,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_node_id: NodeId, features: Option<&'sess Features>, emit_errors: ShouldEmit, - parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option, + parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option, template: &AttributeTemplate, ) -> Option { let ast::AttrKind::Normal(normal_attr) = &attr.kind else { @@ -269,7 +268,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { mut emit_lint: impl FnMut(AttributeLint), ) -> Vec { let mut attributes = Vec::new(); - let mut attr_paths = Vec::new(); + let mut attr_paths: Vec> = Vec::new(); for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. @@ -303,7 +302,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { })) } ast::AttrKind::Normal(n) => { - attr_paths.push(PathParser(Cow::Borrowed(&n.item.path))); + attr_paths.push(PathParser(&n.item.path)); let attr_path = AttrPath::from_ast(&n.item.path, lower_span); self.check_attribute_safety( diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 2304fb0ca63c..09ecfaedb5ed 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -3,7 +3,7 @@ //! //! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs` -use std::borrow::Cow; +use std::borrow::Borrow; use std::fmt::{Debug, Display}; use rustc_ast::token::{self, Delimiter, MetaVarKind}; @@ -26,9 +26,12 @@ use crate::session_diagnostics::{ }; #[derive(Clone, Debug)] -pub struct PathParser<'a>(pub Cow<'a, Path>); +pub struct PathParser>(pub P); -impl<'a> PathParser<'a> { +pub type OwnedPathParser = PathParser; +pub type RefPathParser<'p> = PathParser<&'p Path>; + +impl> PathParser

{ pub fn get_attribute_path(&self) -> hir::AttrPath { AttrPath { segments: self.segments().copied().collect::>().into_boxed_slice(), @@ -36,16 +39,16 @@ impl<'a> PathParser<'a> { } } - pub fn segments(&'a self) -> impl Iterator { - self.0.segments.iter().map(|seg| &seg.ident) + pub fn segments(&self) -> impl Iterator { + self.0.borrow().segments.iter().map(|seg| &seg.ident) } pub fn span(&self) -> Span { - self.0.span + self.0.borrow().span } pub fn len(&self) -> usize { - self.0.segments.len() + self.0.borrow().segments.len() } pub fn segments_is(&self, segments: &[Symbol]) -> bool { @@ -76,21 +79,21 @@ impl<'a> PathParser<'a> { } } -impl Display for PathParser<'_> { +impl> Display for PathParser

{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", pprust::path_to_string(&self.0)) + write!(f, "{}", pprust::path_to_string(self.0.borrow())) } } #[derive(Clone, Debug)] #[must_use] -pub enum ArgParser<'a> { +pub enum ArgParser { NoArgs, - List(MetaItemListParser<'a>), + List(MetaItemListParser), NameValue(NameValueParser), } -impl<'a> ArgParser<'a> { +impl ArgParser { pub fn span(&self) -> Option { match self { Self::NoArgs => None, @@ -100,7 +103,7 @@ impl<'a> ArgParser<'a> { } pub fn from_attr_args<'sess>( - value: &'a AttrArgs, + value: &AttrArgs, parts: &[Symbol], psess: &'sess ParseSess, should_emit: ShouldEmit, @@ -144,7 +147,7 @@ impl<'a> ArgParser<'a> { /// /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list - pub fn list(&self) -> Option<&MetaItemListParser<'a>> { + pub fn list(&self) -> Option<&MetaItemListParser> { match self { Self::List(l) => Some(l), Self::NameValue(_) | Self::NoArgs => None, @@ -184,17 +187,17 @@ impl<'a> ArgParser<'a> { /// /// Choose which one you want using the provided methods. #[derive(Debug, Clone)] -pub enum MetaItemOrLitParser<'a> { - MetaItemParser(MetaItemParser<'a>), +pub enum MetaItemOrLitParser { + MetaItemParser(MetaItemParser), Lit(MetaItemLit), Err(Span, ErrorGuaranteed), } -impl<'sess> MetaItemOrLitParser<'sess> { - pub fn parse_single( +impl MetaItemOrLitParser { + pub fn parse_single<'sess>( parser: &mut Parser<'sess>, should_emit: ShouldEmit, - ) -> PResult<'sess, MetaItemOrLitParser<'static>> { + ) -> PResult<'sess, MetaItemOrLitParser> { let mut this = MetaItemListParserContext { parser, should_emit }; this.parse_meta_item_inner() } @@ -216,7 +219,7 @@ impl<'sess> MetaItemOrLitParser<'sess> { } } - pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> { + pub fn meta_item(&self) -> Option<&MetaItemParser> { match self { MetaItemOrLitParser::MetaItemParser(parser) => Some(parser), _ => None, @@ -238,12 +241,12 @@ impl<'sess> MetaItemOrLitParser<'sess> { /// /// The syntax of MetaItems can be found at #[derive(Clone)] -pub struct MetaItemParser<'a> { - path: PathParser<'a>, - args: ArgParser<'a>, +pub struct MetaItemParser { + path: OwnedPathParser, + args: ArgParser, } -impl<'a> Debug for MetaItemParser<'a> { +impl Debug for MetaItemParser { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("MetaItemParser") .field("path", &self.path) @@ -252,12 +255,12 @@ impl<'a> Debug for MetaItemParser<'a> { } } -impl<'a> MetaItemParser<'a> { +impl MetaItemParser { pub fn span(&self) -> Span { if let Some(other) = self.args.span() { - self.path.span().with_hi(other.hi()) + self.path.borrow().span().with_hi(other.hi()) } else { - self.path.span() + self.path.borrow().span() } } @@ -266,12 +269,12 @@ impl<'a> MetaItemParser<'a> { /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path /// - `#[inline]`: `inline` is a single segment path - pub fn path(&self) -> &PathParser<'a> { + pub fn path(&self) -> &OwnedPathParser { &self.path } /// Gets just the args parser, without caring about the path. - pub fn args(&self) -> &ArgParser<'a> { + pub fn args(&self) -> &ArgParser { &self.args } @@ -281,7 +284,7 @@ impl<'a> MetaItemParser<'a> { /// - `#[inline]`: `inline` is a word /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path, /// and not a word and should instead be parsed using [`path`](Self::path) - pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> { + pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> { self.path().word_is(sym).then(|| self.args()) } } @@ -405,7 +408,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { Ok(lit) } - fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> { + fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> { if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() { return if has_meta_form { let attr_item = self @@ -441,10 +444,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { ArgParser::NoArgs }; - Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args }) + Ok(MetaItemParser { path: PathParser(path), args }) } - fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> { + fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> { if let Some(token_lit) = self.parser.eat_token_lit() { // If a literal token is parsed, we commit to parsing a MetaItemLit for better errors Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?)) @@ -531,7 +534,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { psess: &'sess ParseSess, span: Span, should_emit: ShouldEmit, - ) -> PResult<'sess, MetaItemListParser<'static>> { + ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; @@ -554,14 +557,14 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { } #[derive(Debug, Clone)] -pub struct MetaItemListParser<'a> { - sub_parsers: ThinVec>, +pub struct MetaItemListParser { + sub_parsers: ThinVec, pub span: Span, } -impl<'a> MetaItemListParser<'a> { +impl MetaItemListParser { pub(crate) fn new<'sess>( - tokens: &'a TokenStream, + tokens: &TokenStream, span: Span, psess: &'sess ParseSess, should_emit: ShouldEmit, @@ -570,7 +573,7 @@ impl<'a> MetaItemListParser<'a> { } /// Lets you pick and choose as what you want to parse each element in the list - pub fn mixed(&self) -> impl Iterator> { + pub fn mixed(&self) -> impl Iterator { self.sub_parsers.iter() } @@ -585,7 +588,7 @@ impl<'a> MetaItemListParser<'a> { /// Returns Some if the list contains only a single element. /// /// Inside the Some is the parser to parse this single element. - pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> { + pub fn single(&self) -> Option<&MetaItemOrLitParser> { let mut iter = self.mixed(); iter.next().filter(|_| iter.next().is_none()) } From aa6db80ab20f8b3b0a8bb51217b412ec4f426b3b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 8 Dec 2025 22:56:09 +0100 Subject: [PATCH 56/56] Remove lifetime param from parser functions --- .../src/attributes/allow_unstable.rs | 20 +++--- .../rustc_attr_parsing/src/attributes/cfg.rs | 6 +- .../src/attributes/codegen_attrs.rs | 24 +++---- .../src/attributes/debugger.rs | 8 +-- .../rustc_attr_parsing/src/attributes/doc.rs | 62 +++++++++---------- .../src/attributes/link_attrs.rs | 8 +-- .../rustc_attr_parsing/src/attributes/mod.rs | 8 +-- .../rustc_attr_parsing/src/attributes/repr.rs | 12 ++-- compiler/rustc_attr_parsing/src/interface.rs | 2 +- 9 files changed, 73 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index faa366a62831..79f7171cc0c8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -17,9 +17,9 @@ impl CombineAttributeParser for AllowInternalUnstableParser { ]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, ) -> impl IntoIterator { parse_unstable(cx, args, >::PATH[0]) .into_iter() @@ -39,9 +39,9 @@ impl CombineAttributeParser for UnstableFeatureBoundParser { ]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, ) -> impl IntoIterator { if !cx.features().staged_api() { cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span }); @@ -67,10 +67,10 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { ]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { parse_unstable(cx, args, >::PATH[0]) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 61c314b41b65..8c3896975201 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -35,9 +35,9 @@ const CFG_ATTR_TEMPLATE: AttributeTemplate = template!( "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute" ); -pub fn parse_cfg<'c, S: Stage>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, +pub fn parse_cfg( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, ) -> Option { let ArgParser::List(list) = args else { cx.expected_list(cx.attr_span); diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ce208203ec5e..7d3a7418f06c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -472,10 +472,10 @@ impl AttributeParser for UsedParser { } } -fn parse_tf_attribute<'c, S: Stage>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, -) -> impl IntoIterator + 'c { +fn parse_tf_attribute( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, +) -> impl IntoIterator { let mut features = Vec::new(); let ArgParser::List(list) = args else { cx.expected_list(cx.attr_span); @@ -529,10 +529,10 @@ impl CombineAttributeParser for TargetFeatureParser { }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { parse_tf_attribute(cx, args) } @@ -567,10 +567,10 @@ impl CombineAttributeParser for ForceTargetFeatureParser { Allow(Target::Method(MethodKind::TraitImpl)), ]); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { parse_tf_attribute(cx, args) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index dfb3b914e189..c88b795aab03 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -16,10 +16,10 @@ impl CombineAttributeParser for DebuggerViualizerParser { type Item = DebugVisualizer; const CONVERT: ConvertFn = |v, _| AttributeKind::DebuggerVisualizer(v); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { let Some(l) = args.list() else { cx.expected_list(args.span().unwrap_or(cx.attr_span)); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 547f00d14041..b6fea37c92aa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -10,7 +10,7 @@ use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser}; use crate::context::{AcceptContext, FinalizeContext, Stage}; -use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, PathParser}; +use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttributeNotAttribute, DocKeywordNotKeyword, @@ -43,10 +43,10 @@ fn check_attribute( false } -fn parse_keyword_and_attribute<'c, S, F>( - cx: &'c mut AcceptContext<'_, '_, S>, - path: &PathParser<'_>, - args: &ArgParser<'_>, +fn parse_keyword_and_attribute( + cx: &mut AcceptContext<'_, '_, S>, + path: &OwnedPathParser, + args: &ArgParser, attr_value: &mut Option<(Symbol, Span)>, callback: F, ) where @@ -82,10 +82,10 @@ pub(crate) struct DocParser { } impl DocParser { - fn parse_single_test_doc_attr_item<'c, S: Stage>( + fn parse_single_test_doc_attr_item( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - mip: &'c MetaItemParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + mip: &MetaItemParser, ) { let path = mip.path(); let args = mip.args(); @@ -132,9 +132,9 @@ impl DocParser { } } - fn add_alias<'c, S: Stage>( + fn add_alias( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, alias: Symbol, span: Span, ) { @@ -167,11 +167,11 @@ impl DocParser { self.attribute.aliases.insert(alias, span); } - fn parse_alias<'c, S: Stage>( + fn parse_alias( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - path: &PathParser<'_>, - args: &ArgParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + path: &OwnedPathParser, + args: &ArgParser, ) { match args { ArgParser::NoArgs => { @@ -197,11 +197,11 @@ impl DocParser { } } - fn parse_inline<'c, S: Stage>( + fn parse_inline( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - path: &PathParser<'_>, - args: &ArgParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + path: &OwnedPathParser, + args: &ArgParser, inline: DocInline, ) { if let Err(span) = args.no_args() { @@ -212,11 +212,7 @@ impl DocParser { self.attribute.inline.push((inline, path.span())); } - fn parse_cfg<'c, S: Stage>( - &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, - ) { + fn parse_cfg(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { // This function replaces cases like `cfg(all())` with `true`. fn simplify_cfg(cfg_entry: &mut CfgEntry) { match cfg_entry { @@ -236,11 +232,11 @@ impl DocParser { } } - fn parse_auto_cfg<'c, S: Stage>( + fn parse_auto_cfg( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - path: &PathParser<'_>, - args: &ArgParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + path: &OwnedPathParser, + args: &ArgParser, ) { match args { ArgParser::NoArgs => { @@ -343,10 +339,10 @@ impl DocParser { } } - fn parse_single_doc_attr_item<'c, S: Stage>( + fn parse_single_doc_attr_item( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - mip: &MetaItemParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + mip: &MetaItemParser, ) { let path = mip.path(); let args = mip.args(); @@ -506,10 +502,10 @@ impl DocParser { } } - fn accept_single_doc_attr<'c, S: Stage>( + fn accept_single_doc_attr( &mut self, - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser<'_>, + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, ) { match args { ArgParser::NoArgs => { diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 67471e31b105..fe8f3578fe14 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -62,10 +62,10 @@ impl CombineAttributeParser for LinkParser { ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { let items = match args { ArgParser::List(list) => list, // This is an edgecase added because making this a hard error would break too many crates diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index a26159cb09e7..bd58c44214a2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -315,10 +315,10 @@ pub(crate) trait CombineAttributeParser: 'static { const TEMPLATE: AttributeTemplate; /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c; + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator; } /// Use in combination with [`CombineAttributeParser`]. diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index c7320bf5d96f..4520e4f5dbac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -26,10 +26,10 @@ impl CombineAttributeParser for ReprParser { "https://doc.rust-lang.org/reference/type-layout.html#representations" ); - fn extend<'c>( - cx: &'c mut AcceptContext<'_, '_, S>, - args: &'c ArgParser, - ) -> impl IntoIterator + 'c { + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { let mut reprs = Vec::new(); let Some(list) = args.list() else { @@ -275,7 +275,7 @@ impl AlignParser { const PATH: &'static [Symbol] = &[sym::rustc_align]; const TEMPLATE: AttributeTemplate = template!(List: &[""]); - fn parse<'c, S: Stage>(&mut self, cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser) { + fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { match args { ArgParser::NoArgs | ArgParser::NameValue(_) => { cx.expected_list(cx.attr_span); @@ -332,7 +332,7 @@ impl AlignStaticParser { const PATH: &'static [Symbol] = &[sym::rustc_align_static]; const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; - fn parse<'c, S: Stage>(&mut self, cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser) { + fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { self.0.parse(cx, args) } } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 732f8d6e3a2a..91596ff0de60 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -342,7 +342,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { // blob // a if is_doc_attribute - && let ArgParser::NameValue(nv) = args + && let ArgParser::NameValue(nv) = &args // If not a string key/value, it should emit an error, but to make // things simpler, it's handled in `DocParser` because it's simpler to // emit an error with `AcceptContext`.