diff --git a/Cargo.lock b/Cargo.lock index 2320e33bc4bf1..a838c0775b7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,7 +580,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clippy" -version = "0.1.92" +version = "0.1.93" dependencies = [ "anstream", "askama", @@ -607,7 +607,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.92" +version = "0.1.93" dependencies = [ "clippy_utils", "itertools", @@ -630,7 +630,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.92" +version = "0.1.93" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -662,7 +662,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.92" +version = "0.1.93" dependencies = [ "arrayvec", "itertools", @@ -1066,7 +1066,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.92" +version = "0.1.93" [[package]] name = "derive-where" @@ -1288,7 +1288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2101,9 +2101,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libdbus-sys" @@ -2154,7 +2154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-targets 0.52.6", ] [[package]] @@ -2216,9 +2216,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -4897,15 +4897,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 430ffded2e87a..8f44931826398 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -789,14 +789,14 @@ pub struct PatField { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Encodable, Decodable, HashStable_Generic, Walkable)] pub enum ByRef { - Yes(Mutability), + Yes(Pinnedness, Mutability), No, } impl ByRef { #[must_use] pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { - if let ByRef::Yes(old_mutbl) = &mut self { + if let ByRef::Yes(_, old_mutbl) = &mut self { *old_mutbl = cmp::min(*old_mutbl, mutbl); } self @@ -814,20 +814,33 @@ pub struct BindingMode(pub ByRef, pub Mutability); impl BindingMode { pub const NONE: Self = Self(ByRef::No, Mutability::Not); - pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not); + pub const REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Not); + pub const REF_PIN: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Not); pub const MUT: Self = Self(ByRef::No, Mutability::Mut); - pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not); - pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut); - pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut); + pub const REF_MUT: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Not); + pub const REF_PIN_MUT: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Not); + pub const MUT_REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Mut); + pub const MUT_REF_PIN: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Mut); + pub const MUT_REF_MUT: Self = + Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Mut); + pub const MUT_REF_PIN_MUT: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Mut); pub fn prefix_str(self) -> &'static str { match self { Self::NONE => "", Self::REF => "ref ", + Self::REF_PIN => "ref pin const ", Self::MUT => "mut ", Self::REF_MUT => "ref mut ", + Self::REF_PIN_MUT => "ref pin mut ", Self::MUT_REF => "mut ref ", + Self::MUT_REF_PIN => "mut ref pin ", Self::MUT_REF_MUT => "mut ref mut ", + Self::MUT_REF_PIN_MUT => "mut ref pin mut ", } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d850698f47b08..99490be64175f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -368,6 +368,7 @@ macro_rules! common_visitor_and_walkers { crate::tokenstream::TokenStream, Movability, Mutability, + Pinnedness, Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::fx::FxHashMap, rustc_span::ErrorGuaranteed, diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index 44837b1b49407..0f63fad157431 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -311,3 +311,10 @@ pub enum Pinnedness { Not, Pinned, } + +impl Pinnedness { + /// Return `true` if self is pinned + pub fn is_pinned(self) -> bool { + matches!(self, Self::Pinned) + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a5e2bcaa3bd06..93f4e47342f14 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1712,10 +1712,15 @@ impl<'a> State<'a> { if mutbl.is_mut() { self.word_nbsp("mut"); } - if let ByRef::Yes(rmutbl) = by_ref { + if let ByRef::Yes(pinnedness, rmutbl) = by_ref { self.word_nbsp("ref"); + if pinnedness.is_pinned() { + self.word_nbsp("pin"); + } if rmutbl.is_mut() { self.word_nbsp("mut"); + } else if pinnedness.is_pinned() { + self.word_nbsp("const"); } } self.print_ident(*ident); diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 639c75d7c5e47..093969586596d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -48,6 +48,7 @@ pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; +pub(crate) mod pin_v2; pub(crate) mod proc_macro_attrs; pub(crate) mod prototype; pub(crate) mod repr; diff --git a/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs new file mode 100644 index 0000000000000..597a9515b0048 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs @@ -0,0 +1,21 @@ +use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::Allow; + +pub(crate) struct PinV2Parser; + +impl NoArgsAttributeParser for PinV2Parser { + const PATH: &[Symbol] = &[sym::pin_v2]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Enum), + Allow(Target::Struct), + Allow(Target::Union), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinV2; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6694dac8bb25a..15904fd7d3348 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -47,6 +47,7 @@ use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; +use crate::attributes::pin_v2::PinV2Parser; use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; @@ -233,6 +234,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b4990ffc7739c..e23c7eebeca1e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1188,7 +1188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: BindingMode(ByRef::Yes(_), _), + binding_mode: BindingMode(ByRef::Yes(..), _), .. })) => { let pattern_span: Span = local_decl.source_info.span; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d4d38301a1586..db9df8a84f4e5 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2582,6 +2582,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { _ => bug!("Deref of unexpected type: {:?}", base_ty), } } + // Check as the inner reference type if it is a field projection + // from the `&pin` pattern + ProjectionElem::Field(FieldIdx::ZERO, _) + if let Some(adt) = + place_base.ty(self.body(), self.infcx.tcx).ty.ty_adt_def() + && adt.is_pin() + && self.infcx.tcx.features().pin_ergonomics() => + { + self.is_mutable(place_base, is_local_mutation_allowed) + } // All other projections are owned by their base path, so mutable if // base path is mutable ProjectionElem::Field(..) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a2acac8b3045d..2c472801aa044 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -893,6 +893,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, loop_match, experimental!(loop_match) ), + // The `#[pin_v2]` attribute is part of the `pin_ergonomics` experiment + // that allows structurally pinning, tracked in: + // + // - https://github.com/rust-lang/rust/issues/130494 + gated!( + pin_v2, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2), + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 828c15cc391dd..1bb87673d52d2 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -637,6 +637,9 @@ pub enum AttributeKind { /// Represents `#[pattern_complexity_limit]` PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit }, + /// Represents `#[pin_v2]` + PinV2(Span), + /// Represents `#[pointee]` Pointee(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 362ab407aab35..11c54b0ac1da0 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,6 +77,7 @@ impl AttributeKind { PassByValue(..) => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, + PinV2(..) => Yes, Pointee(..) => No, ProcMacro(..) => No, ProcMacroAttribute(..) => No, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ac7ad669b4611..3272f34a54526 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -13,7 +13,7 @@ use rustc_ast::{ pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, - MetaItemInner, MetaItemLit, Movability, Mutability, UnOp, + MetaItemInner, MetaItemLit, Movability, Mutability, Pinnedness, UnOp, }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index f99fefcf56ace..bac34454b3b7a 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -575,7 +575,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true, + PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(..), _), ..) => true, PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)), diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 3dede69ce1063..cadbc54c34108 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -12,11 +12,12 @@ use std::assert_matches::debug_assert_matches; use min_specialization::check_min_specialization; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_errors::codes::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::ErrorGuaranteed; +use rustc_span::{ErrorGuaranteed, kw}; use crate::constrained_generic_params as cgp; use crate::errors::UnconstrainedGenericParameter; @@ -150,6 +151,27 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( const_param_note2: false, }); diag.code(E0207); + for p in &impl_generics.own_params { + if p.name == kw::UnderscoreLifetime { + let span = tcx.def_span(p.def_id); + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else { + continue; + }; + + let (span, sugg) = if &snippet == "'_" { + (span, param.name.to_string()) + } else { + (span.shrink_to_hi(), format!("{} ", param.name)) + }; + diag.span_suggestion_verbose( + span, + "consider using the named lifetime here instead of an implicit \ + lifetime", + sugg, + Applicability::MaybeIncorrect, + ); + } + } res = Err(diag.emit()); } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 12d981396c8a4..5bf449a97dcd2 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1925,10 +1925,15 @@ impl<'a> State<'a> { if mutbl.is_mut() { self.word_nbsp("mut"); } - if let ByRef::Yes(rmutbl) = by_ref { + if let ByRef::Yes(pinnedness, rmutbl) = by_ref { self.word_nbsp("ref"); + if pinnedness.is_pinned() { + self.word_nbsp("pin"); + } if rmutbl.is_mut() { self.word_nbsp("mut"); + } else if pinnedness.is_pinned() { + self.word_nbsp("const"); } } self.print_ident(ident); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 1ed0756fdd6a7..60303d5ee81e4 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -227,6 +227,10 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules +hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_v2]` + .note = type defined here + .suggestion = add `#[pin_v2]` here + hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> [1] auto trait {$traits} *[other] auto traits {$traits} diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index d15d092b7d3da..615a8c95056d1 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1156,3 +1156,14 @@ pub(crate) struct ConstContinueBadLabel { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_project_on_non_pin_project_type)] +pub(crate) struct ProjectOnNonPinProjectType { + #[primary_span] + pub span: Span, + #[note] + pub def_span: Option, + #[suggestion(code = "#[pin_v2]\n", applicability = "machine-applicable")] + pub sugg_span: Option, +} diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 2034131882820..b9f1463d8007d 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -986,7 +986,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // of the pattern, as this just looks confusing, instead use the span // of the discriminant. match bm.0 { - hir::ByRef::Yes(m) => { + hir::ByRef::Yes(_, m) => { let bk = ty::BorrowKind::from_mutbl(m); self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); } @@ -1004,7 +1004,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Deref patterns on boxes don't borrow, so we ignore them here. // HACK: this could be a fake pattern corresponding to a deref inserted by match // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. - if let hir::ByRef::Yes(mutability) = + if let hir::ByRef::Yes(_, mutability) = self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern) { let bk = ty::BorrowKind::from_mutbl(mutability); @@ -1256,7 +1256,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx .get(pat.hir_id) .expect("missing binding mode"); - if matches!(bm.0, hir::ByRef::Yes(_)) { + if let hir::ByRef::Yes(pinnedness, _) = bm.0 { + let base_ty = if pinnedness.is_pinned() { + base_ty.pinned_ty().ok_or_else(|| { + debug!("By-pin-ref binding of non-`Pin` type: {base_ty:?}"); + self.cx.report_bug(pat.span, "by-pin-ref binding of non-`Pin` type") + })? + } else { + base_ty + }; // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. @@ -1264,7 +1272,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { Some(ty) => Ok(ty), None => { - debug!("By-ref binding of non-derefable type"); + debug!("By-ref binding of non-derefable type: {base_ty:?}"); Err(self .cx .report_bug(pat.span, "by-ref binding of non-derefable type")) @@ -1706,6 +1714,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx }; self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)? } + adjustment::PatAdjust::PinDeref => { + debug!("`PinDeref` of non-pinned-reference type: {:?}", adjust.source); + let target_ty = adjust.source.pinned_ty().ok_or_else(|| { + self.cx.report_bug( + self.cx.tcx().hir_span(pat.hir_id), + "`PinDeref` of non-pinned-reference type", + ) + })?; + let kind = ProjectionKind::Field(FieldIdx::ZERO, FIRST_VARIANT); + place_with_id = self.cat_projection(pat.hir_id, place_with_id, target_ty, kind); + self.cat_deref(pat.hir_id, place_with_id)? + } }; } drop(typeck_results); // explicitly release borrow of typeck results, just in case. @@ -1877,7 +1897,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Deref patterns on boxes are lowered using a built-in deref. hir::ByRef::No => self.cat_deref(hir_id, base_place), // For other types, we create a temporary to match on. - hir::ByRef::Yes(mutability) => { + hir::ByRef::Yes(_, mutability) => { let re_erased = self.cx.tcx().lifetimes.re_erased; let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d14463e44a03f..f27085d59c0ed 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -18,7 +18,7 @@ use rustc_hir::{ use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::traits::PatternOriginExpr; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::parse::feature_err; @@ -403,7 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); self.write_ty(pat.hir_id, ty); - // If we implicitly inserted overloaded dereferences before matching, check the pattern to + // If we implicitly inserted overloaded dereferences before matching check the pattern to // see if the dereferenced types need `DerefMut` bounds. if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) @@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat, derefed_tys.iter().filter_map(|adjust| match adjust.kind { PatAdjust::OverloadedDeref => Some(adjust.source), - PatAdjust::BuiltinDeref => None, + PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, }), ); } @@ -471,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { #[cfg(debug_assertions)] - if pat_info.binding_mode == ByRef::Yes(Mutability::Mut) + if matches!(pat_info.binding_mode, ByRef::Yes(_, Mutability::Mut)) && pat_info.max_ref_mutbl != MutblCap::Mut && self.downgrade_mut_inside_shared() { @@ -489,12 +489,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_pat_info = pat_info; let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info }; + let adjust_binding_mode = |inner_pinnedness, inner_mutability| { + match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability), + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + // Pinnedness is preserved. + ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability), + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + // Pinnedness is preserved. + ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not), + } + }; + match pat.kind { - // Peel off a `&` or `&mut` from the scrutinee type. See the examples in + // Peel off a `&` or `&mut`from the scrutinee type. See the examples in // `tests/ui/rfcs/rfc-2005-default-binding-mode`. _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode && pat.default_binding_modes - && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && let &ty::Ref(_, inner_ty, inner_mutability) = expected.kind() && self.should_peel_ref(peel_kind, expected) => { debug!("inspecting {:?}", expected); @@ -508,22 +524,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .or_default() .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected }); - let mut binding_mode = ByRef::Yes(match pat_info.binding_mode { - // If default binding mode is by value, make it `ref` or `ref mut` - // (depending on whether we observe `&` or `&mut`). - ByRef::No | - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ByRef::Yes(Mutability::Mut) => inner_mutability, - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - ByRef::Yes(Mutability::Not) => Mutability::Not, - }); + let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability); let mut max_ref_mutbl = pat_info.max_ref_mutbl; if self.downgrade_mut_inside_shared() { binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); } - if binding_mode == ByRef::Yes(Mutability::Not) { + if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) { max_ref_mutbl = MutblCap::Not; } debug!("default binding mode is now {:?}", binding_mode); @@ -533,6 +540,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Recurse with the new expected type. self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) } + // If `pin_ergonomics` is enabled, peel the `&pin` from the pinned reference type. See the + // examples in `tests/ui/async-await/pin-ergonomics/`. + _ if self.tcx.features().pin_ergonomics() + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat.default_binding_modes + && self.should_peel_smart_pointer(peel_kind, expected) + && let Some(pinned_ty) = expected.pinned_ty() + // Currently, only pinned reference is specially handled, leaving other + // pinned types (e.g. `Pin>` to deref patterns) handled as a + // deref pattern. + && let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() => + { + debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); + + // if the inner_ty is an ADT, make sure that it can be structurally pinned + // (i.e., it is `#[pin_v2]`). + if let Some(adt) = inner_ty.ty_adt_def() + && !adt.is_pin_project() + && !adt.is_pin() + { + let def_span: Option = self.tcx.hir_span_if_local(adt.did()); + let sugg_span = def_span.map(|span| span.shrink_to_lo()); + self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { + span: pat.span, + def_span, + sugg_span, + }); + } + + let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); + // If the pinnedness is `Not`, it means the pattern is unpinned + // and thus requires an `Unpin` bound. + if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) { + self.register_bound( + inner_ty, + self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), + self.misc(pat.span), + ) + } + debug!("default binding mode is now {:?}", binding_mode); + + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = PatInfo { binding_mode, ..old_pat_info }; + + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::PinDeref, + new_pat_info, + ) + } // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the // examples in `tests/ui/pattern/deref_patterns/`. _ if self.tcx.features().deref_patterns() @@ -540,35 +601,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && pat.default_binding_modes && self.should_peel_smart_pointer(peel_kind, expected) => { - debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref"); + debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref"); + // The scrutinee is a smart pointer; implicitly dereference it. This adds a // requirement that `expected: DerefPure`. - let mut inner_ty = self.deref_pat_target(pat.span, expected); + let inner_ty = self.deref_pat_target(pat.span, expected); // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. - let mut typeck_results = self.typeck_results.borrow_mut(); - let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); - let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); - // We may reach the recursion limit if a user matches on a type `T` satisfying - // `T: Deref`; error gracefully in this case. - // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move - // this check out of this branch. Alternatively, this loop could be implemented with - // autoderef and this check removed. For now though, don't break code compiling on - // stable with lots of `&`s and a low recursion limit, if anyone's done that. - if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { - // Preserve the smart pointer type for THIR lowering and closure upvar analysis. - pat_adjustments - .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected }); - } else { - let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); - inner_ty = Ty::new_error(self.tcx, guar); - } - drop(typeck_results); - - // Recurse, using the old pat info to keep `current_depth` to its old value. - // Peeling smart pointers does not update the default binding mode. - self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info) + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::OverloadedDeref, + old_pat_info, + ) } PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. @@ -647,6 +696,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_deref_pattern( + &self, + pat: &'tcx Pat<'tcx>, + opt_path_res: Option, ErrorGuaranteed>>, + adjust_mode: AdjustMode, + expected: Ty<'tcx>, + mut inner_ty: Ty<'tcx>, + pat_adjust_kind: PatAdjust, + pat_info: PatInfo<'tcx>, + ) -> Ty<'tcx> { + debug_assert!( + !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref), + "unexpected deref pattern for builtin reference type {expected:?}", + ); + + let mut typeck_results = self.typeck_results.borrow_mut(); + let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); + let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected }); + } else { + let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); + inner_ty = Ty::new_error(self.tcx, guar); + } + drop(typeck_results); + + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info) + } + /// How should the binding mode and expected type be adjusted? /// /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. @@ -1061,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { - BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_, def_br_mutbl) = def_br => { // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and // using other experimental matching features compatible with it. if pat.span.at_least_rust_2024() @@ -1091,8 +1178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(user_br_mutbl), _) => { - if let ByRef::Yes(def_br_mutbl) = def_br { + BindingMode(ByRef::Yes(_, user_br_mutbl), _) => { + if let ByRef::Yes(_, def_br_mutbl) = def_br { // `ref`/`ref mut` overrides the binding mode on edition <= 2021 self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, @@ -1108,7 +1195,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if bm.0 == ByRef::Yes(Mutability::Mut) + if matches!(bm.0, ByRef::Yes(_, Mutability::Mut)) && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl { let mut err = struct_span_code_err!( @@ -1136,7 +1223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_ty = self.local_ty(pat.span, pat.hir_id); let eq_ty = match bm.0 { - ByRef::Yes(mutbl) => { + ByRef::Yes(Pinnedness::Not, mutbl) => { // If the binding is like `ref x | ref mut x`, // then `x` is assigned a value of type `&M T` where M is the // mutability and T is the expected type. @@ -1146,6 +1233,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // See (note_1) for an explanation. self.new_ref_ty(pat.span, mutbl, expected) } + // Wrapping the type into `Pin` if the binding is like `ref pin const|mut x` + ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt( + self.tcx, + self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)), + self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]), + ), // Otherwise, the type of x is the expected type `T`. ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; @@ -2605,7 +2698,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected = self.try_structurally_resolve_type(pat.span, expected); // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. - if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode + // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here + // should be adjusted to `pat_pin == inh_pin` + && (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not) + { match self.ref_pat_matches_inherited_ref(pat.span.edition()) { InheritedRefMatchRule::EatOuter => { // ref pattern attempts to consume inherited reference @@ -3126,8 +3223,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the user-provided binding modifier doesn't match the default binding mode, we'll // need to suggest reference patterns, which can affect other bindings. // For simplicity, we opt to suggest making the pattern fully explicit. - info.suggest_eliding_modes &= - user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not); + info.suggest_eliding_modes &= matches!( + user_bind_annot, + BindingMode(ByRef::Yes(_, mutbl), Mutability::Not) if mutbl == def_br_mutbl + ); if user_bind_annot == BindingMode(ByRef::No, Mutability::Mut) { info.bad_mut_modifiers = true; "`mut` binding modifier" diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index c9fdb61e9c2cb..5aacf1f8d960e 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -7,7 +7,7 @@ use std::{env, thread}; use rustc_ast as ast; use rustc_attr_parsing::{ShouldEmit, validate_attr}; -use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder; +use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::target_features::{self, cfg_target_feature}; use rustc_codegen_ssa::traits::CodegenBackend; @@ -440,7 +440,7 @@ impl CodegenBackend for DummyCodegenBackend { link_binary( sess, - &ArArchiveBuilderBuilder, + &DummyArchiveBuilderBuilder, codegen_results, metadata, outputs, @@ -449,6 +449,28 @@ impl CodegenBackend for DummyCodegenBackend { } } +struct DummyArchiveBuilderBuilder; + +impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder { + fn new_archive_builder<'a>( + &self, + sess: &'a Session, + ) -> Box { + ArArchiveBuilderBuilder.new_archive_builder(sess) + } + + fn create_dll_import_lib( + &self, + sess: &Session, + _lib_name: &str, + _items: Vec, + output_path: &Path, + ) { + // Build an empty static library to avoid calling an external dlltool on mingw + ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path); + } +} + // This is used for rustdoc, but it uses similar machinery to codegen backend // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 16e1fb0192b32..1c0df1f4234a2 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for StaticMutRefs { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) { if let hir::StmtKind::Let(loc) = stmt.kind && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind - && let hir::ByRef::Yes(m) = ba.0 + && let hir::ByRef::Yes(_, m) = ba.0 && let Some(init) = loc.init && let Some(err_span) = path_is_static_mut(init, init.span) { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 74573455f531a..2920c9cb42ab4 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -232,4 +232,7 @@ pub enum PatAdjust { /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the /// pattern `[..]` against a scrutinee of type `Vec`. OverloadedDeref, + /// An implicit dereference before matching a `&pin` reference (under feature `pin_ergonomics`), + /// which will be lowered as a builtin deref of the private field `__pointer` in `Pin` + PinDeref, } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index df82c7a826be9..510c546f82a4e 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -55,6 +55,10 @@ bitflags::bitflags! { const IS_UNSAFE_CELL = 1 << 9; /// Indicates whether the type is `UnsafePinned`. const IS_UNSAFE_PINNED = 1 << 10; + /// Indicates whether the type is `Pin`. + const IS_PIN = 1 << 11; + /// Indicates whether the type is `#[pin_project]`. + const IS_PIN_PROJECT = 1 << 12; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -284,6 +288,10 @@ impl AdtDefData { debug!("found non-exhaustive variant list for {:?}", did); flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } + if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinV2(..)) { + debug!("found pin-project type {:?}", did); + flags |= AdtFlags::IS_PIN_PROJECT; + } flags |= match kind { AdtKind::Enum => AdtFlags::IS_ENUM, @@ -313,6 +321,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::UnsafePinned) { flags |= AdtFlags::IS_UNSAFE_PINNED; } + if tcx.is_lang_item(did, LangItem::Pin) { + flags |= AdtFlags::IS_PIN; + } AdtDefData { did, variants, flags, repr } } @@ -428,6 +439,19 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_MANUALLY_DROP) } + /// Returns `true` if this is `Pin`. + #[inline] + pub fn is_pin(self) -> bool { + self.flags().contains(AdtFlags::IS_PIN) + } + + /// Returns `true` is this is `#[pin_v2]` for the purposes + /// of structural pinning. + #[inline] + pub fn is_pin_project(self) -> bool { + self.flags().contains(AdtFlags::IS_PIN_PROJECT) + } + /// Returns `true` if this type has a destructor. pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool { self.destructor(tcx).is_some() diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ad8b1fd0693b0..953c806658aef 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1349,6 +1349,23 @@ impl<'tcx> Ty<'tcx> { } } + pub fn pinned_ty(self) -> Option> { + match self.kind() { + Adt(def, args) if def.is_pin() => Some(args.type_at(0)), + _ => None, + } + } + + pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { + if let Adt(def, args) = self.kind() + && def.is_pin() + && let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind() + { + return Some((ty, mutbl)); + } + None + } + /// Panics if called on any type other than `Box`. pub fn expect_boxed_ty(self) -> Ty<'tcx> { self.boxed_ty() diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 20657ba8c726c..96f5697971a47 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -11,6 +11,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::hir_id::OwnerId; use rustc_hir::{ self as hir, BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, + Pinnedness, }; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; @@ -479,7 +480,7 @@ impl<'tcx> TypeckResults<'tcx> { let mut has_ref_mut = false; pat.walk(|pat| { if let hir::PatKind::Binding(_, id, _, _) = pat.kind - && let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + && let Some(BindingMode(ByRef::Yes(_, Mutability::Mut), _)) = self.pat_binding_modes().get(id) { has_ref_mut = true; @@ -503,7 +504,7 @@ impl<'tcx> TypeckResults<'tcx> { ByRef::No } else { let mutable = self.pat_has_ref_mut_binding(inner); - ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not }) + ByRef::Yes(Pinnedness::Not, if mutable { Mutability::Mut } else { Mutability::Not }) } } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index c3028b9b5c4ea..af565ef636993 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,9 +1,10 @@ use std::sync::Arc; +use rustc_abi::FieldIdx; use rustc_hir::ByRef; use rustc_middle::mir::*; use rustc_middle::thir::*; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt}; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; @@ -299,7 +300,24 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => { + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { + let Some(ref_ty) = pattern.ty.pinned_ty() else { + rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty); + }; + MatchPairTree::for_pattern( + place_builder.field(FieldIdx::ZERO, ref_ty).deref(), + subpattern, + cx, + &mut subpairs, + extra_data, + ); + None + } + + PatKind::DerefPattern { + ref subpattern, + borrow: ByRef::Yes(Pinnedness::Not, mutability), + } => { // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? let temp = cx.temp( diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 82f12a969bb1c..96c58dc14e8cb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -5,20 +5,21 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. +use std::assert_matches::debug_assert_matches; use std::borrow::Borrow; use std::mem; use std::sync::Arc; use itertools::{Itertools, Position}; -use rustc_abi::VariantIdx; +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; -use rustc_middle::bug; +use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; +use rustc_middle::{bug, span_bug}; use rustc_pattern_analysis::constructor::RangeEnd; use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt}; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; @@ -917,6 +918,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visit_subpat(self, subpattern, &user_tys.deref(), f); } + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { + visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f); + } + PatKind::DerefPattern { ref subpattern, .. } => { visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } @@ -2747,9 +2752,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } - ByRef::Yes(mutbl) => { - // The arm binding will be by reference, so eagerly create it now. Drops must - // be scheduled to emit `StorageDead` on the guard's failure/break branches. + ByRef::Yes(pinnedness, mutbl) => { + // The arm binding will be by reference, so eagerly create it now // be scheduled to emit `StorageDead` on the guard's failure/break branches. let value_for_arm = self.storage_live_binding( block, binding.var_id, @@ -2761,6 +2765,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + let rvalue = match pinnedness { + ty::Pinnedness::Not => rvalue, + ty::Pinnedness::Pinned => { + self.pin_borrowed_local(block, value_for_arm.local, rvalue, source_info) + } + }; self.cfg.push_assign(block, source_info, value_for_arm, rvalue); // For the guard binding, take a shared reference to that reference. let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); @@ -2797,14 +2807,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let rvalue = match binding.binding_mode.0 { ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), - ByRef::Yes(mutbl) => { - Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source) + ByRef::Yes(pinnedness, mutbl) => { + let rvalue = + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + match pinnedness { + ty::Pinnedness::Not => rvalue, + ty::Pinnedness::Pinned => { + self.pin_borrowed_local(block, local.local, rvalue, source_info) + } + } } }; self.cfg.push_assign(block, source_info, local, rvalue); } } + /// Given an rvalue `&[mut]borrow` and a local `local`, generate the pinned borrow for it: + /// ```ignore (illustrative) + /// pinned_temp = &borrow; + /// local = Pin { __pointer: move pinned_temp }; + /// ``` + fn pin_borrowed_local( + &mut self, + block: BasicBlock, + local: Local, + borrow: Rvalue<'tcx>, + source_info: SourceInfo, + ) -> Rvalue<'tcx> { + debug_assert_matches!(borrow, Rvalue::Ref(..)); + + let local_ty = self.local_decls[local].ty; + + let pinned_ty = local_ty.pinned_ty().unwrap_or_else(|| { + span_bug!( + source_info.span, + "expect type `Pin` for a pinned binding, found type {:?}", + local_ty + ) + }); + let pinned_temp = + Place::from(self.local_decls.push(LocalDecl::new(pinned_ty, source_info.span))); + self.cfg.push_assign(block, source_info, pinned_temp, borrow); + Rvalue::Aggregate( + Box::new(AggregateKind::Adt( + self.tcx.require_lang_item(LangItem::Pin, source_info.span), + FIRST_VARIANT, + self.tcx.mk_args(&[pinned_ty.into()]), + None, + None, + )), + std::iter::once(Operand::Move(pinned_temp)).collect(), + ) + } + /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The /// first local is a binding for occurrences of `var` in the guard, which diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 195d45c2c4c49..18e9543407764 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -383,7 +383,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } visit::walk_pat(self, pat); } - PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => { + PatKind::Binding { mode: BindingMode(ByRef::Yes(_, rm), _), ty, .. } => { if self.inside_adt { let ty::Ref(_, ty, _) = ty.kind() else { span_bug!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 3929a97eed8f2..2400d297c89cf 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -797,7 +797,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); sub.each_binding(|_, mode, _, span| { - if matches!(mode, ByRef::Yes(_)) { + if matches!(mode, ByRef::Yes(..)) { conflicts_ref.push(span) } }); @@ -813,7 +813,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: return; } ByRef::No => return, - ByRef::Yes(m) => m, + ByRef::Yes(_, m) => m, }; // We now have `ref $mut_outer binding @ sub` (semantically). @@ -823,7 +823,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let mut conflicts_mut_ref = Vec::new(); sub.each_binding(|name, mode, ty, span| { match mode { - ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) { + ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) { // Both sides are `ref`. (Mutability::Not, Mutability::Not) => {} // 2x `ref mut`. diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index 8887308530506..095023a471b93 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -212,7 +212,7 @@ impl<'a> PatMigration<'a> { } if !self.info.suggest_eliding_modes && explicit_ba.0 == ByRef::No - && let ByRef::Yes(mutbl) = mode.0 + && let ByRef::Yes(_, mutbl) = mode.0 { // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern // fully explicit. i.e. we'll need to suggest reference patterns for this. diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index dc6b958904497..404f410e22191 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer}; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, LangItem, RangeEnd}; +use rustc_hir::{self as hir, ByRef, LangItem, Mutability, Pinnedness, RangeEnd}; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::LitToConstInput; @@ -114,6 +114,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); PatKind::DerefPattern { subpattern: thir_pat, borrow } } + PatAdjust::PinDeref => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(pat); + PatKind::DerefPattern { + subpattern: thir_pat, + borrow: ByRef::Yes( + Pinnedness::Pinned, + if mutable { Mutability::Mut } else { Mutability::Not }, + ), + } + } }; Box::new(Pat { span, ty: adjust.source, kind }) }); @@ -354,11 +364,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; - if let hir::ByRef::Yes(_) = mode.0 { - if let ty::Ref(_, rty, _) = ty.kind() { - ty = *rty; - } else { - bug!("`ref {}` has wrong type {}", ident, ty); + if let hir::ByRef::Yes(pinnedness, _) = mode.0 { + match pinnedness { + hir::Pinnedness::Pinned + if let Some(pty) = ty.pinned_ty() + && let &ty::Ref(_, rty, _) = pty.kind() => + { + debug_assert!( + self.tcx.features().pin_ergonomics(), + "`pin_ergonomics` must be enabled to have a by-pin-ref binding" + ); + ty = rty; + } + hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => { + ty = rty; + } + _ => bug!("`ref {}` has wrong type {}", ident, ty), } }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ed4069dae933c..30870c810942f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::tokenstream::{ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, - Visibility, VisibilityKind, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Pinnedness, Recovered, + Safety, StrLit, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1317,7 +1317,12 @@ impl<'a> Parser<'a> { /// Parses reference binding mode (`ref`, `ref mut`, or nothing). fn parse_byref(&mut self) -> ByRef { - if self.eat_keyword(exp!(Ref)) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } + if self.eat_keyword(exp!(Ref)) { + // FIXME(pin_ergonomics): support `ref pin const|mut` bindings + ByRef::Yes(Pinnedness::Not, self.parse_mutability()) + } else { + ByRef::No + } } /// Possibly parses mutability (`const` or `mut`). diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 4f522d57e4140..f964ecb90326f 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -7,7 +7,8 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, - Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, + Pat, PatField, PatFieldsRest, PatKind, Path, Pinnedness, QSelf, RangeEnd, RangeSyntax, Stmt, + StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -778,7 +779,11 @@ impl<'a> Parser<'a> { } // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)? + self.parse_pat_ident( + // FIXME(pin_ergonomics): support `ref pin const|mut` bindings + BindingMode(ByRef::Yes(Pinnedness::Not, mutbl), Mutability::Not), + syntax_loc, + )? } else if self.eat_keyword(exp!(Box)) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -1093,7 +1098,7 @@ impl<'a> Parser<'a> { self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); } - if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) { + if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..)) { self.psess.gated_spans.gate(sym::mut_ref, pat.span); } Ok(pat.kind) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ef42c42f68b37..c214104d60670 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -283,7 +283,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ObjcSelector { .. } | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::DebuggerVisualizer(..) - | AttributeKind::RustcMain, + | AttributeKind::RustcMain + | AttributeKind::PinV2(..), ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e3051dc38eca2..91d0cae062a7e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -19,7 +19,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, + Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, + pluralize, }; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; @@ -377,6 +378,7 @@ enum LifetimeBinderKind { Function, Closure, ImplBlock, + ImplAssocType, } impl LifetimeBinderKind { @@ -387,6 +389,7 @@ impl LifetimeBinderKind { PolyTrait => "bound", WhereBound => "bound", Item | ConstItem => "item", + ImplAssocType => "associated type", ImplBlock => "impl block", Function => "function", Closure => "closure", @@ -1874,9 +1877,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ty: ty.span, }); } else { - self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError { - lifetime: lifetime.ident.span, - }); + let mut err = self.r.dcx().create_err( + errors::AnonymousLifetimeNonGatReportError { + lifetime: lifetime.ident.span, + }, + ); + self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span); + err.emit(); } } else { self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError { @@ -1913,6 +1920,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } + fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) { + let Some((rib, span)) = self.lifetime_ribs[..i] + .iter() + .rev() + .skip(1) + .filter_map(|rib| match rib.kind { + LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => { + Some((rib, span)) + } + _ => None, + }) + .next() + else { + return; + }; + if !rib.bindings.is_empty() { + err.span_label( + span, + format!( + "there {} named lifetime{} specified on the impl block you could use", + if rib.bindings.len() == 1 { "is a" } else { "are" }, + pluralize!(rib.bindings.len()), + ), + ); + if rib.bindings.len() == 1 { + err.span_suggestion_verbose( + lifetime.shrink_to_hi(), + "consider using the lifetime from the impl block", + format!("{} ", rib.bindings.keys().next().unwrap()), + Applicability::MaybeIncorrect, + ); + } + } else { + err.span_label( + span, + "you could add a lifetime on the impl block, if the trait or the self type can \ + have one", + ); + } + } + #[instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); @@ -3352,7 +3400,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &generics.params, RibKind::AssocItem, item.id, - LifetimeBinderKind::Item, + LifetimeBinderKind::ImplAssocType, generics.span, |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9e82237694e3c..57f81ebf0d81e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3178,6 +3178,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { continue; } + if let LifetimeBinderKind::ImplAssocType = kind { + continue; + } if !span.can_be_used_for_suggestions() && suggest_note diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 60d56b1b808df..0516982aeabb9 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -27,10 +27,8 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] -# FIXME: Remove this pin once this rustix issue is resolved -# https://github.com/bytecodealliance/rustix/issues/1496 # tidy-alphabetical-start -libc = "=0.2.174" +libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c1c9fa57f85b2..9e8465d2da2e5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1668,6 +1668,7 @@ symbols! { pin, pin_ergonomics, pin_macro, + pin_v2, platform_intrinsics, plugin, plugin_registrar, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index 478726fbef699..158b38e514016 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "aarch64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("ARM64 Linux with musl 1.2.3".into()), + description: Some("ARM64 Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs index 3919a5e0771b7..d05f4c79b01fe 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "arm-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv6 Linux with musl 1.2.3".into()), + description: Some("Armv6 Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs index ca52e5b3ca6c7..9ce752efd6aa2 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "arm-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Armv6 Linux with musl 1.2.3, hardfloat".into()), + description: Some("Armv6 Linux with musl 1.2.5, hardfloat".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs index e675739629b59..0240450b61d30 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv5te-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv5TE Linux with musl 1.2.3".into()), + description: Some("Armv5TE Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs index 42fbf6f486197..dd8a97bf77f65 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv7-A Linux with musl 1.2.3".into()), + description: Some("Armv7-A Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs index a3ac0223c84bf..c3b7ac5f994c2 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Armv7-A Linux with musl 1.2.3, hardfloat".into()), + description: Some("Armv7-A Linux with musl 1.2.5, hardfloat".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs index 1abf0537cda77..74a7eab1e0c48 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "hexagon-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("Hexagon Linux with musl 1.2.3".into()), + description: Some("Hexagon Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index 47a7eb3d597b4..b6b85f5236d80 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -31,7 +31,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "i686-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("32-bit Linux with musl 1.2.3".into()), + description: Some("32-bit Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index 508abc0101841..eb1148d5e8fa1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 for OpenWrt Linux musl 1.2.3".into()), + description: Some("MIPS64 for OpenWrt Linux musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index 94ecd3590a93a..e54628acb1ad2 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()), + description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index 38c3c7dfaa1bb..a256733734f7f 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()), + description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs index 82f2fda7fff0a..2d258d7e438ff 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "mips-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS Linux with musl 1.2.3".into()), + description: Some("MIPS Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs index d008bb55189be..f4fb6be332166 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "mipsel-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS (little endian) Linux with musl 1.2.3".into()), + description: Some("MIPS (little endian) Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 482b6790dadcd..8fb991d0bd1a7 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit PowerPC Linux with musl 1.2.3".into()), + description: Some("64-bit PowerPC Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 26ee6a68c6a8b..0f8b78fa7307d 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), + description: Some("64-bit PowerPC Linux with musl 1.2.5, Little Endian".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index f39142d01018b..f5c7cb061988a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("PowerPC Linux with musl 1.2.3".into()), + description: Some("PowerPC Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index eb592cca1c81d..a13bb173e2483 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -6,9 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "riscv32-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some( - "RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches".into(), - ), + description: Some("RISC-V Linux (kernel 5.4, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 70c19952af063..83fd4e2cfa9ee 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "riscv64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("RISC-V Linux (kernel 4.20, musl 1.2.3)".into()), + description: Some("RISC-V Linux (kernel 4.20, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 0cdbb626739d0..509105afedcd2 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "s390x-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("S390x Linux (kernel 3.2, musl 1.2.3)".into()), + description: Some("S390x Linux (kernel 3.2, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index e026595439f48..d58339bc38c29 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3".into()), + description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs index a5723341fe64c..f5bc3ab707db6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit Unikraft with musl 1.2.3".into()), + description: Some("64-bit Unikraft with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index cc5f88862400e..3cb2a962a5624 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit Linux with musl 1.2.3".into()), + description: Some("64-bit Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index dc70089c385fe..288ee6357a510 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -386,7 +386,6 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), - ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("apxf", Unstable(sym::apx_target_feature), &[]), ("avx", Stable, &["sse4.2"]), ("avx2", Stable, &["avx"]), diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 8988126bd90c0..c2c18889dde7d 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -125,7 +125,7 @@ impl Barrier { let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id); + self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id); BarrierWaitResult(false) } else { lock.count = 0; diff --git a/library/std/src/sync/nonpoison/condvar.rs b/library/std/src/sync/nonpoison/condvar.rs index 994fc6816a0d0..d2b251d7c44c1 100644 --- a/library/std/src/sync/nonpoison/condvar.rs +++ b/library/std/src/sync/nonpoison/condvar.rs @@ -1,4 +1,5 @@ use crate::fmt; +use crate::ops::DerefMut; use crate::sync::WaitTimeoutResult; use crate::sync::nonpoison::{MutexGuard, mutex}; use crate::sys::sync as sys; @@ -38,7 +39,7 @@ use crate::time::{Duration, Instant}; /// let (lock, cvar) = &*pair; /// let mut started = lock.lock(); /// while !*started { -/// started = cvar.wait(started); +/// cvar.wait(&mut started); /// } /// ``` /// @@ -115,16 +116,15 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + pub fn wait(&self, guard: &mut MutexGuard<'_, T>) { unsafe { - let lock = mutex::guard_lock(&guard); + let lock = mutex::guard_lock(guard); self.inner.wait(lock); } - guard } /// Blocks the current thread until the provided condition becomes false. @@ -167,21 +167,17 @@ impl Condvar { /// // Wait for the thread to start up. /// let (lock, cvar) = &*pair; /// // As long as the value inside the `Mutex` is `true`, we wait. - /// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending }); + /// let mut guard = lock.lock(); + /// cvar.wait_while(&mut guard, |pending| { *pending }); /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - mut condition: F, - ) -> MutexGuard<'a, T> + pub fn wait_while(&self, guard: &mut MutexGuard<'_, T>, mut condition: F) where F: FnMut(&mut T) -> bool, { - while condition(&mut *guard) { - guard = self.wait(guard); + while condition(guard.deref_mut()) { + self.wait(guard); } - guard } /// Waits on this condition variable for a notification, timing out after a @@ -206,7 +202,7 @@ impl Condvar { /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. /// - /// Like [`wait`], the lock specified will be re-acquired when this function + /// Like [`wait`], the lock specified will have been re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. /// /// [`wait`]: Self::wait @@ -239,9 +235,8 @@ impl Condvar { /// let mut started = lock.lock(); /// // as long as the value inside the `Mutex` is `false`, we wait /// loop { - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)); + /// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10)); /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; /// if *started == true { /// // We received the notification and the value has been updated, we can leave. /// break @@ -249,16 +244,16 @@ impl Condvar { /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout<'a, T>( + pub fn wait_timeout( &self, - guard: MutexGuard<'a, T>, + guard: &mut MutexGuard<'_, T>, dur: Duration, - ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { + ) -> WaitTimeoutResult { let success = unsafe { - let lock = mutex::guard_lock(&guard); + let lock = mutex::guard_lock(guard); self.inner.wait_timeout(lock, dur) }; - (guard, WaitTimeoutResult(!success)) + WaitTimeoutResult(!success) } /// Waits on this condition variable for a notification, timing out after a @@ -277,7 +272,7 @@ impl Condvar { /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed without the condition being met. /// - /// Like [`wait_while`], the lock specified will be re-acquired when this + /// Like [`wait_while`], the lock specified will have been re-acquired when this /// function returns, regardless of whether the timeout elapsed or not. /// /// [`wait_while`]: Self::wait_while @@ -307,37 +302,39 @@ impl Condvar { /// /// // wait for the thread to start up /// let (lock, cvar) = &*pair; + /// let mut guard = lock.lock(); /// let result = cvar.wait_timeout_while( - /// lock.lock(), + /// &mut guard, /// Duration::from_millis(100), /// |&mut pending| pending, /// ); - /// if result.1.timed_out() { + /// if result.timed_out() { /// // timed-out without the condition ever evaluating to false. /// } - /// // access the locked mutex via result.0 + /// // access the locked mutex via guard /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout_while<'a, T, F>( + pub fn wait_timeout_while( &self, - mut guard: MutexGuard<'a, T>, + guard: &mut MutexGuard<'_, T>, dur: Duration, mut condition: F, - ) -> (MutexGuard<'a, T>, WaitTimeoutResult) + ) -> WaitTimeoutResult where F: FnMut(&mut T) -> bool, { let start = Instant::now(); - loop { - if !condition(&mut *guard) { - return (guard, WaitTimeoutResult(false)); - } + + while condition(guard.deref_mut()) { let timeout = match dur.checked_sub(start.elapsed()) { Some(timeout) => timeout, - None => return (guard, WaitTimeoutResult(true)), + None => return WaitTimeoutResult(true), }; - guard = self.wait_timeout(guard, timeout).0; + + self.wait_timeout(guard, timeout); } + + WaitTimeoutResult(false) } /// Wakes up one blocked thread on this condvar. @@ -378,7 +375,7 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] @@ -422,7 +419,7 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 42b880e283afe..e5a7ad8f9b331 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -17,256 +17,469 @@ nonpoison_and_poison_unwrap_test!( } ); +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: notify_one, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_notify_one() { + use std::sync::poison::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); - let g = maybe_unwrap(m.lock()); - let _t = thread::spawn(move || { - let _g = maybe_unwrap(m2.lock()); - c2.notify_one(); + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + c2.notify_one(); + }); + + let g = c.wait(g).unwrap(); + drop(g); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_notify_one() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let mut g = m.lock(); + let _t = thread::spawn(move || { + let _g = m2.lock(); + c2.notify_one(); + }); + + c.wait(&mut g); + drop(g); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_notify_all() { + use std::sync::poison::{Condvar, Mutex}; + + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock().unwrap(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cnt = cond.wait(cnt).unwrap(); + } + tx.send(()).unwrap(); }); - let g = maybe_unwrap(c.wait(g)); - drop(g); } -); + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock().unwrap(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + for _ in 0..N { + rx.recv().unwrap(); + } +} + +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: notify_all, - test_body: { - use locks::{Condvar, Mutex}; - - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = maybe_unwrap(lock.lock()); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cnt = maybe_unwrap(cond.wait(cnt)); - } +fn nonpoison_notify_all() { + use std::sync::nonpoison::{Condvar, Mutex}; + + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock(); + *cnt += 1; + if *cnt == N { tx.send(()).unwrap(); - }); - } - drop(tx); + } + while *cnt != 0 { + cond.wait(&mut cnt); + } + tx.send(()).unwrap(); + }); + } + drop(tx); - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = maybe_unwrap(lock.lock()); - *cnt = 0; - cond.notify_all(); - drop(cnt); + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock(); + *cnt = 0; + cond.notify_all(); + drop(cnt); - for _ in 0..N { - rx.recv().unwrap(); - } + for _ in 0..N { + rx.recv().unwrap(); } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: test_mutex_arc_condvar, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_test_mutex_arc_condvar() { + use std::sync::poison::{Condvar, Mutex}; - struct Packet(Arc<(Mutex, Condvar)>); + struct Packet(Arc<(Mutex, Condvar)>); - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); + let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // Wait until our parent has taken the lock. - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock().unwrap(); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = lock.lock().unwrap(); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + guard = cvar.wait(guard).unwrap(); + } +} - // Set the data to `true` and wake up our parent. - let mut guard = maybe_unwrap(lock.lock()); - *guard = true; - cvar.notify_one(); - }); +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_test_mutex_arc_condvar() { + use std::sync::nonpoison::{Condvar, Mutex}; - let &(ref lock, ref cvar) = &*packet.0; - let mut guard = maybe_unwrap(lock.lock()); - // Wake up our child. - tx.send(()).unwrap(); + struct Packet(Arc<(Mutex, Condvar)>); - // Wait until our child has set the data to `true`. - assert!(!*guard); - while !*guard { - guard = maybe_unwrap(cvar.wait(guard)); - } + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock(); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = lock.lock(); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + cvar.wait(&mut guard); } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_while, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_wait_while() { + use std::sync::poison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started).unwrap(); + assert!(*guard); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_wait_while() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let mut guard = lock.lock(); + cvar.wait_while(&mut guard, |started| !*started); + assert!(*guard); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_wait() { + use std::sync::poison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair2 = pair.clone(); + break; + } +} - // Inside of our lock, spawn a new thread, and then wait for it to start. - thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; - let mut started = maybe_unwrap(lock.lock()); - *started = true; - // We notify the condvar that the value has changed. - cvar.notify_one(); - }); +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_wait_timeout_wait() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let mut g = m.lock(); + let no_timeout = c.wait_timeout(&mut g, Duration::from_millis(1)); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } - // Wait for the thread to start up. - let &(ref lock, ref cvar) = &*pair; - let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started); - assert!(*maybe_unwrap(guard)); + break; } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_wait, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_wait_timeout_while_wait() { + use std::sync::poison::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - loop { - let g = maybe_unwrap(m.lock()); - let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1))); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not timeout - if !no_timeout.timed_out() { - continue; - } + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} - break; - } - } -); +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_wait_timeout_while_wait() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + let mut g = m.lock(); + let wait = c.wait_timeout_while(&mut g, Duration::from_millis(1), |_| true); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} + +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_wait, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_wait_timeout_while_instant_satisfy() { + use std::sync::poison::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let g = maybe_unwrap(m.lock()); - let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true)); - // no spurious wakeups. ensure it timed-out - assert!(wait.timed_out()); - } -); + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_instant_satisfy, - test_body: { - use locks::{Condvar, Mutex}; +fn nonpoison_wait_timeout_while_instant_satisfy() { + use std::sync::nonpoison::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let g = maybe_unwrap(m.lock()); - let (_g, wait) = - maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false)); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - } -); + let mut g = m.lock(); + let wait = c.wait_timeout_while(&mut g, Duration::from_millis(0), |_| false); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_wake, - test_body: { - use locks::{Condvar, Mutex}; +fn poison_wait_timeout_while_wake() { + use std::sync::poison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + + let (g2, wait) = c + .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) + .unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_wait_timeout_while_wake() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let mut g = m.lock(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + + let wait = + c.wait_timeout_while(&mut g, Duration::from_millis(u64::MAX), |&mut notified| !notified); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_wake() { + use std::sync::poison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair_copy = pair.clone(); + loop { + let g = m.lock().unwrap(); - let &(ref m, ref c) = &*pair; - let g = maybe_unwrap(m.lock()); - let _t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair_copy; - let mut started = maybe_unwrap(lock.lock()); + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); + + let t = thread::spawn(move || { + let _g = m2.lock().unwrap(); thread::sleep(Duration::from_millis(1)); - *started = true; - cvar.notify_one(); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); }); - let (g2, wait) = maybe_unwrap(c.wait_timeout_while( - g, - Duration::from_millis(u64::MAX), - |&mut notified| !notified - )); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - assert!(*g2); + + let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { + t.join().unwrap(); + continue; + } + drop(g); + + t.join().unwrap(); + + break; } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_wake, - test_body: { - use locks::{Condvar, Mutex}; +fn nonpoison_wait_timeout_wake() { + use std::sync::nonpoison::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - loop { - let g = maybe_unwrap(m.lock()); - - let c2 = c.clone(); - let m2 = m.clone(); - - let notified = Arc::new(AtomicBool::new(false)); - let notified_copy = notified.clone(); - - let t = thread::spawn(move || { - let _g = maybe_unwrap(m2.lock()); - thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::Relaxed); - c2.notify_one(); - }); - let (g, timeout_res) = - maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX))); - assert!(!timeout_res.timed_out()); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not notified - if !notified.load(Ordering::Relaxed) { - t.join().unwrap(); - continue; - } - drop(g); + loop { + let mut g = m.lock(); - t.join().unwrap(); + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); - break; + let t = thread::spawn(move || { + let _g = m2.lock(); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); + }); + + let timeout_res = c.wait_timeout(&mut g, Duration::from_millis(u64::MAX)); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { + t.join().unwrap(); + continue; } + drop(g); + + t.join().unwrap(); + + break; } -); +} // Some platforms internally cast the timeout duration into nanoseconds. // If they fail to consider overflow during the conversion (I'm looking @@ -274,42 +487,112 @@ nonpoison_and_poison_unwrap_test!( // timeout for durations that are slightly longer than u64::MAX nanoseconds. // `std` should guard against this by clamping the timeout. // See #37440 for context. -nonpoison_and_poison_unwrap_test!( - name: timeout_nanoseconds, - test_body: { - use locks::Mutex; - use locks::Condvar; +#[test] +fn poison_timeout_nanoseconds() { + use std::sync::poison::{Condvar, Mutex}; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + // Sleep so that the other thread has a chance to encounter the + // timeout. + thread::sleep(Duration::from_secs(2)); + *sent.lock().unwrap() = true; + cond.notify_all(); + }); - let sent = Mutex::new(false); - let cond = Condvar::new(); - - thread::scope(|s| { - s.spawn(|| { - // Sleep so that the other thread has a chance to encounter the - // timeout. - thread::sleep(Duration::from_secs(2)); - maybe_unwrap(sent.set(true)); - cond.notify_all(); - }); - - let mut guard = maybe_unwrap(sent.lock()); - // Loop until `sent` is set by the thread to guard against spurious - // wakeups. If the `wait_timeout` happens just before the signal by - // the other thread, such a spurious wakeup might prevent the - // miscalculated timeout from occurring, but this is basically just - // a smoke test anyway. - loop { - if *guard { - break; - } - - // If there is internal overflow, this call will return almost - // immediately, before the other thread has reached the `notify_all`, - // and indicate a timeout. - let (g, res) = maybe_unwrap(cond.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))); - assert!(!res.timed_out()); - guard = g; + let mut guard = sent.lock().unwrap(); + // Loop until `sent` is set by the thread to guard against spurious + // wakeups. If the `wait_timeout` happens just before the signal by + // the other thread, such a spurious wakeup might prevent the + // miscalculated timeout from occurring, but this is basically just + // a smoke test anyway. + loop { + if *guard { + break; } - }) + + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all`, + // and indicate a timeout. + let (g, res) = cond + .wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000))) + .unwrap(); + assert!(!res.timed_out()); + guard = g; + } + }) +} + +#[test] +fn nonpoison_timeout_nanoseconds() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + // Sleep so that the other thread has a chance to encounter the + // timeout. + thread::sleep(Duration::from_secs(2)); + sent.set(true); + cond.notify_all(); + }); + + let mut guard = sent.lock(); + // Loop until `sent` is set by the thread to guard against spurious + // wakeups. If the `wait_timeout` happens just before the signal by + // the other thread, such a spurious wakeup might prevent the + // miscalculated timeout from occurring, but this is basically just + // a smoke test anyway. + loop { + if *guard { + break; + } + + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all`, + // and indicate a timeout. + let res = cond + .wait_timeout(&mut guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000))); + assert!(!res.timed_out()); + } + }) +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_arc_condvar_poison() { + use std::sync::poison::{Condvar, Mutex}; + + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || -> () { + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let _g = lock.lock().unwrap(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + while *lock == 1 { + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } } -); +} diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index ff6aef717936f..75a6bf64607ef 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -3,7 +3,7 @@ use std::ops::FnMut; use std::panic::{self, AssertUnwindSafe}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::channel; -use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::sync::{Arc, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; use std::{hint, mem, thread}; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -423,38 +423,6 @@ fn test_replace_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_arc_condvar_poison() { - struct Packet(Arc<(Mutex, Condvar)>); - - let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - - let _t = thread::spawn(move || -> () { - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let _g = lock.lock().unwrap(); - cvar.notify_one(); - // Parent should fail when it wakes up. - panic!(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - while *lock == 1 { - match cvar.wait(lock) { - Ok(l) => { - lock = l; - assert_eq!(*lock, 1); - } - Err(..) => break, - } - } -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_poison() { diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index bd749b88f566d..e4318bedb2a6f 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -93,7 +93,6 @@ features! { /// * `"amx-fp8"` /// * `"amx-movrs"` /// * `"amx-tf32"` - /// * `"amx-transpose"` /// * `"f16c"` /// * `"fma"` /// * `"bmi1"` @@ -231,8 +230,6 @@ features! { /// AMX-MOVRS (Matrix MOVERS operations) @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32"; /// AMX-TF32 (TensorFloat32 Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_transpose: "amx-transpose"; - /// AMX-TRANSPOSE (Matrix Transpose Operations) @FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf"; /// APX-F (Advanced Performance Extensions - Foundation) @FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1"; diff --git a/library/std_detect/src/detect/os/x86.rs b/library/std_detect/src/detect/os/x86.rs index cf11d8333127f..18925da2b2755 100644 --- a/library/std_detect/src/detect/os/x86.rs +++ b/library/std_detect/src/detect/os/x86.rs @@ -285,7 +285,6 @@ pub(crate) fn detect_features() -> cache::Initializer { unsafe { __cpuid_count(0x1e_u32, 1) }; enable(amx_feature_flags_eax, 4, Feature::amx_fp8); - enable(amx_feature_flags_eax, 5, Feature::amx_transpose); enable(amx_feature_flags_eax, 6, Feature::amx_tf32); enable(amx_feature_flags_eax, 7, Feature::amx_avx512); enable(amx_feature_flags_eax, 8, Feature::amx_movrs); diff --git a/library/std_detect/tests/x86-specific.rs b/library/std_detect/tests/x86-specific.rs index 2ed2bb2a99ecd..90ca32208e78d 100644 --- a/library/std_detect/tests/x86-specific.rs +++ b/library/std_detect/tests/x86-specific.rs @@ -76,7 +76,6 @@ fn dump() { println!("widekl: {:?}", is_x86_feature_detected!("widekl")); println!("movrs: {:?}", is_x86_feature_detected!("movrs")); println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8")); - println!("amx-transpose: {:?}", is_x86_feature_detected!("amx-transpose")); println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32")); println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512")); println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs")); diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig index e7afdbe9d4dea..014930052b4ac 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig @@ -11,3 +11,4 @@ CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y CT_CC_LANG_CXX=y +CT_MUSL_V_1_2_5=y diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig index 01fe493a9fecd..f5f3cfd4b49f3 100644 --- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig @@ -12,3 +12,4 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="--enable-compressed-debug-sections=none" CT_GLIBC_V_2_17=y CT_GCC_V_8=y CT_CC_LANG_CXX=y +CT_MUSL_V_1_2_5=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig index c6cde30b2a4e5..db2b5533947cd 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig @@ -11,6 +11,6 @@ CT_ARCH_ARCH="powerpc64le" CT_KERNEL_LINUX=y CT_LINUX_V_4_19=y CT_LIBC_MUSL=y -CT_MUSL_V_1_2_3=y +CT_MUSL_V_1_2_5=y CT_CC_LANG_CXX=y CT_GETTEXT_NEEDED=y diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index bc1b30e2d31ae..511521a2b49da 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -4,7 +4,7 @@ # # Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and # musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions. -# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.3. +# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.5. # ignore-tidy-linelength @@ -45,11 +45,11 @@ export CFLAGS="-fPIC -g1 $CFLAGS" git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9 cd musl-cross-make -# A version that includes support for building musl 1.2.3 -git checkout fe915821b652a7fa37b34a596f47d8e20bc72338 +# A version that includes support for building musl 1.2.5 +git checkout 3635262e4524c991552789af6f36211a335a77b3 -hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER -hide_output make install TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT +hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER +hide_output make install TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT cd - diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 9878bec6fbe8c..7e293d748ce16 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -25,7 +25,7 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -MUSL=musl-1.2.3 +MUSL=musl-1.2.5 # may have been downloaded in a previous run if [ ! -d $MUSL ]; then diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 99739ee734e4c..6f26125cf3454 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -89,7 +89,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI -[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.3 +[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17) @@ -101,14 +101,14 @@ target | notes `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2+, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2+, glibc 2.17) [`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10+, glibc 2.17) -[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.3) +[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.5) [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20+, glibc 2.29) [`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2+, glibc 2.17) [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) [`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | 64-bit x86 MinGW (Windows 10+), LLVM ABI [`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD [`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos -`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 +`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.5 [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | x86_64 OpenHarmony [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 [`x86_64-pc-solaris`](platform-support/solaris.md) | 64-bit x86 Solaris 11.4 @@ -153,26 +153,26 @@ target | std | notes [`aarch64-unknown-none-softfloat`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, softfloat [`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI [`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android -`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 -`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat +`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.5 +`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.5, hardfloat [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23) -`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 +`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.5 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android [`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27) -`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 -`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat +`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.5 +`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.5, hardfloat [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A [`armv7a-none-eabihf`](platform-support/armv7a-none-eabi.md) | * | | Bare Armv7-A, hardfloat [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87] -`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.5, original Pentium) [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] [`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] -`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.5 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) @@ -182,7 +182,7 @@ target | std | notes [`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) [`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) [`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) -[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.3) +[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.5) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4+, glibc 2.23) @@ -312,7 +312,7 @@ target | std | host | notes `bpfel-unknown-none` | * | | BPF (little endian) `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) -[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 +[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.5 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86 (original Pentium) [^x86_32-floats-x87] @@ -334,17 +334,17 @@ target | std | host | notes [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) -`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 +`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.5 `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc -[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.3 +[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.5 `mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23) -[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.3 +[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.5 `mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23) -`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3 +`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.5 `mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) [`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX) [`mipsel-unknown-linux-gnu`](platform-support/mipsel-unknown-linux-gnu.md) | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23) -`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.3 +`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.5 `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support `mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat @@ -357,15 +357,15 @@ target | std | host | notes `msp430-none-elf` | * | | 16-bit MSP430 microcontrollers [`powerpc-unknown-freebsd`](platform-support/freebsd.md) | ? | | PowerPC FreeBSD [`powerpc-unknown-linux-gnuspe`](platform-support/powerpc-unknown-linux-gnuspe.md) | ✓ | | PowerPC SPE Linux -`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 -[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.3 +`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.5 +[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.5 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) [`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2) -[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.3) +[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.5) [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD @@ -374,7 +374,7 @@ target | std | host | notes [`riscv32em-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EM ISA) [`riscv32emc-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EMC ISA) `riscv32gc-unknown-linux-gnu` | ✓ | | RISC-V Linux (kernel 5.4, glibc 2.33) -`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches) +`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.5) [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) [`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF @@ -396,7 +396,7 @@ target | std | host | notes [`riscv64gc-unknown-redox`](platform-support/redox.md) | ✓ | | RISC-V 64bit Redox OS [`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`riscv64a23-unknown-linux-gnu`](platform-support/riscv64a23-unknown-linux-gnu.md) | ✓ | ✓ | RISC-V Linux (kernel 6.8.0+, glibc 2.39) -[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3) +[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.5) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ [`sparc64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/sparc64 @@ -411,7 +411,7 @@ target | std | host | notes [`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX [`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX, hardfloat [`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7M with NuttX -`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 +`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.5 [`thumbv8m.base-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Baseline with NuttX [`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX [`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat @@ -425,7 +425,7 @@ target | std | host | notes [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | [`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) | [`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 8.0 RTOS | -[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 +[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.5 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit diff --git a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md index c604b487c2c26..b8bee11055fe5 100644 --- a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md @@ -13,7 +13,7 @@ IBM z/Architecture (s390x) targets (including IBM Z and LinuxONE) running Linux. This target requires: * Linux Kernel version 3.2 or later -* musl 1.2.3 or later +* musl 1.2.5 or later Code generated by the target uses the z/Architecture ISA assuming a minimum architecture level of z10 (Eighth Edition of the z/Architecture Principles diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index c37736f137df9..0363418f91647 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -343,7 +343,7 @@ struct TokenHandler<'a, 'tcx, F: Write> { /// We need to keep the `Class` for each element because it could contain a `Span` which is /// used to generate links. href_context: Option>, - write_line_number: fn(u32) -> String, + line_number_kind: LineNumberKind, line: u32, max_lines: u32, } @@ -355,10 +355,10 @@ impl std::fmt::Debug for TokenHandler<'_, '_, F> { } impl<'a, F: Write> TokenHandler<'a, '_, F> { - fn handle_backline(&mut self) -> Option { + fn handle_backline(&mut self) -> Option> { self.line += 1; if self.line < self.max_lines { - return Some((self.write_line_number)(self.line)); + return Some(self.line_number_kind.render(self.line)); } None } @@ -376,8 +376,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> { if text == "\n" && let Some(backline) = self.handle_backline() { - self.out.write_str(&text).unwrap(); - self.out.write_str(&backline).unwrap(); + write!(self.out, "{text}{backline}").unwrap(); } else { self.push_token_without_backline_check(class, text, true); } @@ -437,20 +436,29 @@ impl Drop for TokenHandler<'_, '_, F> { } } -fn scraped_line_number(line: u32) -> String { - // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - // Do not show "1 2 3 4 5 ..." in web search results. - format!("{line}") -} - -fn line_number(line: u32) -> String { - // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - // Do not show "1 2 3 4 5 ..." in web search results. - format!("{line}") +/// Represents the type of line number to be generated as HTML. +#[derive(Clone, Copy)] +enum LineNumberKind { + /// Used for scraped code examples. + Scraped, + /// Used for source code pages. + Normal, + /// Code examples in documentation don't have line number generated by rustdoc. + Empty, } -fn empty_line_number(_: u32) -> String { - String::new() +impl LineNumberKind { + fn render(self, line: u32) -> impl Display { + fmt::from_fn(move |f| { + match self { + // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr + // Do not show "1 2 3 4 5 ..." in web search results. + Self::Scraped => write!(f, "{line}"), + Self::Normal => write!(f, "{line}"), + Self::Empty => Ok(()), + } + }) + } } fn get_next_expansion( @@ -534,15 +542,15 @@ pub(super) fn write_code( let mut token_handler = TokenHandler { out, href_context, - write_line_number: match line_info { + line_number_kind: match line_info { Some(line_info) => { if line_info.is_scraped_example { - scraped_line_number + LineNumberKind::Scraped } else { - line_number + LineNumberKind::Normal } } - None => empty_line_number, + None => LineNumberKind::Empty, }, line: 0, max_lines: u32::MAX, @@ -552,8 +560,12 @@ pub(super) fn write_code( if let Some(line_info) = line_info { token_handler.line = line_info.start_line - 1; token_handler.max_lines = line_info.max_lines; - if let Some(text) = token_handler.handle_backline() { - token_handler.push_token_without_backline_check(None, Cow::Owned(text), false); + if let Some(backline) = token_handler.handle_backline() { + token_handler.push_token_without_backline_check( + None, + Cow::Owned(backline.to_string()), + false, + ); } } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 0a73d32dac2de..e281139ca38ff 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3906,6 +3906,8 @@ class DocSearch { return name === "traitalias"; case "macro": return name === "attr" || name === "derive"; + case "import": + return name === "externcrate"; } // No match diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 37d46d3496677..6cb2755be0eec 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -8,6 +8,89 @@ document. [e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) +## Rust 1.91 + +Current stable, released 2025-10-30 + +[View all 146 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-07-25T21%3A05%3A11Z..2025-09-04T22%3A34%3A27Z+base%3Amaster) + +### New Lints + +* Added [`possible_missing_else`] to `suspicious` + [#15317](https://github.com/rust-lang/rust-clippy/pull/15317) + +### Moves and Deprecations + +* Moved [`cognitive_complexity`] from `nursery` to `restriction` + [#15415](https://github.com/rust-lang/rust-clippy/pull/15415) +* Moved [`declare_interior_mutable_const`] from `style` to `suspicious` + [#15454](https://github.com/rust-lang/rust-clippy/pull/15454) +* Moved [`crosspointer_transmute`] from `complexity` to `suspicious` + [#15403](https://github.com/rust-lang/rust-clippy/pull/15403) + +### Enhancements + +* [`excessive_precision`] added `const_literal_digits_threshold` option to suppress overly precise constants. + [#15193](https://github.com/rust-lang/rust-clippy/pull/15193) +* [`unwrap_in_result`] rewritten for better accuracy; now lints on `.unwrap()` and `.expect()` + directly and no longer mixes `Result` and `Option`. + [#15445](https://github.com/rust-lang/rust-clippy/pull/15445) +* [`panic`] now works in `const` contexts. + [#15565](https://github.com/rust-lang/rust-clippy/pull/15565) +* [`implicit_clone`] now also lints `to_string` calls (merging [`string_to_string`] behavior). + [#14177](https://github.com/rust-lang/rust-clippy/pull/14177) +* [`collapsible_match`] improved suggestions to handle necessary ref/dereferencing. + [#14221](https://github.com/rust-lang/rust-clippy/pull/14221) +* [`map_identity`] now suggests making variables mutable when required; recognizes tuple struct restructuring. + [#15261](https://github.com/rust-lang/rust-clippy/pull/15261) +* [`option_map_unit_fn`] preserves `unsafe` blocks in suggestions. + [#15570](https://github.com/rust-lang/rust-clippy/pull/15570) +* [`unnecessary_mut_passed`] provides structured, clearer fix suggestions. + [#15438](https://github.com/rust-lang/rust-clippy/pull/15438) +* [`float_equality_without_abs`] now checks `f16` and `f128` types. + [#15054](https://github.com/rust-lang/rust-clippy/pull/15054) +* [`doc_markdown`] expanded whitelist (`InfiniBand`, `RoCE`, `PowerPC`) and improved handling of + identifiers like NixOS. + [#15558](https://github.com/rust-lang/rust-clippy/pull/15558) +* [`clone_on_ref_ptr`] now suggests fully qualified paths to avoid resolution errors. + [#15561](https://github.com/rust-lang/rust-clippy/pull/15561) +* [`manual_assert`] simplifies boolean expressions in suggested fixes. + [#15368](https://github.com/rust-lang/rust-clippy/pull/15368) +* [`four_forward_slashes`] warns about bare CR in comments and avoids invalid autofixes. + [#15175](https://github.com/rust-lang/rust-clippy/pull/15175) + +### False Positive Fixes + +* [`alloc_instead_of_core`] fixed FP when `alloc` is an alias + [#15581](https://github.com/rust-lang/rust-clippy/pull/15581) +* [`needless_range_loop`] fixed FP and FN when meeting multidimensional array + [#15486](https://github.com/rust-lang/rust-clippy/pull/15486) +* [`semicolon_inside_block`] fixed FP when attribute over expr is not enabled + [#15476](https://github.com/rust-lang/rust-clippy/pull/15476) +* [`unnested_or_patterns`] fixed FP on structs with only shorthand field patterns + [#15343](https://github.com/rust-lang/rust-clippy/pull/15343) +* [`match_ref_pats`] fixed FP on match scrutinee of never type + [#15474](https://github.com/rust-lang/rust-clippy/pull/15474) +* [`infinite_loop`] fixed FP in async blocks that are not awaited + [#15157](https://github.com/rust-lang/rust-clippy/pull/15157) +* [`iter_on_single_items`] fixed FP on function pointers and let statements + [#15013](https://github.com/rust-lang/rust-clippy/pull/15013) + +### ICE Fixes + +* [`len_zero`] fix ICE when fn len has a return type without generic type params + [#15660](https://github.com/rust-lang/rust-clippy/pull/15660) + +### Documentation Improvements + +* [`cognitive_complexity`] corrected documentation to state lint is in `restriction`, not `nursery` + [#15563](https://github.com/rust-lang/rust-clippy/pull/15563) + +### Performance Improvements + +* [`doc_broken_link`] optimized by 99.77% (12M → 27k instructions) + [#15385](https://github.com/rust-lang/rust-clippy/pull/15385) + ## Rust 1.90 Current stable, released 2025-09-18 @@ -6253,6 +6336,7 @@ Released 2018-09-13 [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets +[`empty_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enums [`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop @@ -6583,6 +6667,7 @@ Released 2018-09-13 [`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if +[`needless_ifs`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_ifs [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match diff --git a/src/tools/clippy/CODE_OF_CONDUCT.md b/src/tools/clippy/CODE_OF_CONDUCT.md index e3708bc485399..133072e0586b4 100644 --- a/src/tools/clippy/CODE_OF_CONDUCT.md +++ b/src/tools/clippy/CODE_OF_CONDUCT.md @@ -1,3 +1,3 @@ # The Rust Code of Conduct -The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). +The Code of Conduct for this repository [can be found online](https://rust-lang.org/policies/code-of-conduct/). diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index bedcc300f8567..fee885d8fa7e9 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.92" +version = "0.1.93" 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/src/tools/clippy/book/book.toml b/src/tools/clippy/book/book.toml index c918aadf83c4b..ae5fd07487ced 100644 --- a/src/tools/clippy/book/book.toml +++ b/src/tools/clippy/book/book.toml @@ -1,8 +1,6 @@ [book] authors = ["The Rust Clippy Developers"] language = "en" -multilingual = false -src = "src" title = "Clippy Documentation" [rust] diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index cca6d6ae7bf34..f3912f81d859f 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -21,14 +21,13 @@ use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, _, _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name == sym::our_fancy_method - // We can check the type of the self argument whenever necessary. - // (It's necessary if we want to check that method is specifically belonging to a specific trait, - // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) + // Check if the method belongs to the `sym::OurFancyTrait` trait. + // (for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -45,6 +44,12 @@ New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sy This module extends the list of symbols already provided by the compiler crates in `rustc_span::sym`. +If a trait defines only one method (such as the `std::ops::Deref` trait, which only has the `deref()` method), +one might be tempted to omit the method name check. This would work, but is not always advisable because: +- If a new method (possibly with a default implementation) were to be added to the trait, there would be a risk of + matching the wrong method. +- Comparing symbols is very cheap and might prevent a more expensive lookup. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index b9ecff1fa3643..6569bdabf115a 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -859,6 +859,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) +* [`len_zero`](https://rust-lang.github.io/rust-clippy/master/index.html#len_zero) * [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok) * [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index f8c748290e418..3f6b26d3334ed 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.92" +version = "0.1.93" edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 9993765b4bdc4..2a042e6c3d853 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -748,6 +748,7 @@ define_Conf! { io_other_error, iter_kv_map, legacy_numeric_constants, + len_zero, lines_filter_map_ok, manual_abs_diff, manual_bits, diff --git a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs index 3bdc5b2772327..0401cfda7080c 100644 --- a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs +++ b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs @@ -1,4 +1,5 @@ -use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::parse::{DeprecatedLint, Lint, ParseCx}; +use crate::update_lints::generate_lint_files; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; @@ -13,21 +14,20 @@ use std::{fs, io}; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - if let Some((prefix, _)) = name.split_once("::") { - panic!("`{name}` should not contain the `{prefix}` prefix"); - } - - let mut lints = find_lint_decls(); - let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); +pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, reason: &'cx str) { + let mut lints = cx.find_lint_decls(); + let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); let Some(lint) = lints.iter().find(|l| l.name == name) else { eprintln!("error: failed to find lint `{name}`"); return; }; - let prefixed_name = String::from_iter(["clippy::", name]); - match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + let prefixed_name = cx.str_buf.with(|buf| { + buf.extend(["clippy::", name]); + cx.arena.alloc_str(buf) + }); + match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { Ok(_) => { println!("`{name}` is already deprecated"); return; @@ -36,8 +36,8 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { idx, DeprecatedLint { name: prefixed_name, - reason: reason.into(), - version: clippy_version.rust_display().to_string(), + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }, ), } @@ -61,8 +61,8 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { } } -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec) { +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) -> io::Result { + fn remove_lint(name: &str, lints: &mut Vec>) { lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); } @@ -135,14 +135,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io ); assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + content[lint.declaration_range].contains(&name.to_uppercase()), "error: `{}` does not contain lint `{}`'s declaration", path.display(), lint.name ); // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); + content.replace_range(lint.declaration_range, ""); // Remove the module declaration (mod xyz;) let mod_decl = format!("\nmod {name};"); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 2b2138d3108df..781e37e6144ef 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -268,7 +268,7 @@ fn run_rustfmt(update_mode: UpdateMode) { .expect("invalid rustfmt path"); rustfmt_path.truncate(rustfmt_path.trim_end().len()); - let args: Vec<_> = walk_dir_no_dot_or_target() + let args: Vec<_> = walk_dir_no_dot_or_target(".") .filter_map(|e| { let e = expect_action(e, ErrAction::Read, "."); e.path() diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 16f413e0c862f..dcca08aee7e64 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,9 +1,12 @@ #![feature( - rustc_private, exit_status_error, if_let_guard, + new_range, + new_range_api, os_str_slice, os_string_truncate, + pattern, + rustc_private, slice_split_once )] #![warn( @@ -15,6 +18,7 @@ )] #![allow(clippy::missing_panics_doc)] +extern crate rustc_arena; #[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; @@ -32,5 +36,8 @@ pub mod setup; pub mod sync; pub mod update_lints; +mod parse; mod utils; -pub use utils::{ClippyInfo, UpdateMode}; + +pub use self::parse::{ParseCx, new_parse_cx}; +pub use self::utils::{ClippyInfo, UpdateMode}; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 1b6a590b896f4..392c3aabf1937 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -4,10 +4,9 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, - update_lints, + ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, + setup, sync, update_lints, }; -use std::convert::Infallible; use std::env; fn main() { @@ -28,7 +27,7 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)), + DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))), DevCommand::NewLint { pass, name, @@ -36,7 +35,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => update_lints::update(UpdateMode::Change), + Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { @@ -79,13 +78,18 @@ fn main() { old_name, new_name, uplift, - } => rename_lint::rename( - clippy.version, - &old_name, - new_name.as_ref().unwrap_or(&old_name), - uplift, - ), - DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), + } => new_parse_cx(|cx| { + rename_lint::rename( + cx, + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ); + }), + DevCommand::Deprecate { name, reason } => { + new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason)); + }, DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, @@ -95,6 +99,20 @@ fn main() { } } +fn lint_name(name: &str) -> Result { + let name = name.replace('-', "_"); + if let Some((pre, _)) = name.split_once("::") { + Err(format!("lint name should not contain the `{pre}` prefix")) + } else if name + .bytes() + .any(|x| !matches!(x, b'_' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z')) + { + Err("lint name contains invalid characters".to_owned()) + } else { + Ok(name) + } +} + #[derive(Parser)] #[command(name = "dev", about)] struct Dev { @@ -150,7 +168,7 @@ enum DevCommand { #[arg( short, long, - value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")), + value_parser = lint_name, )] /// Name of the new lint in snake case, ex: `fn_too_long` name: String, @@ -223,8 +241,12 @@ enum DevCommand { /// Rename a lint RenameLint { /// The name of the lint to rename + #[arg(value_parser = lint_name)] old_name: String, - #[arg(required_unless_present = "uplift")] + #[arg( + required_unless_present = "uplift", + value_parser = lint_name, + )] /// The new name of the lint new_name: Option, #[arg(long)] @@ -234,6 +256,7 @@ enum DevCommand { /// Deprecate the given lint Deprecate { /// The name of the lint to deprecate + #[arg(value_parser = lint_name)] name: String, #[arg(long, short)] /// The reason for deprecation diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index a14afd8c5f416..a180db6ad0629 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,4 +1,5 @@ -use crate::utils::{RustSearcher, Token, Version}; +use crate::parse::cursor::{self, Capture, Cursor}; +use crate::utils::Version; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -516,22 +517,22 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { #[allow(clippy::enum_glob_use)] - use Token::*; + use cursor::Pat::*; let mut context = None; let mut decl_end = None; - let mut searcher = RustSearcher::new(contents); - while let Some(name) = searcher.find_capture_token(CaptureIdent) { - match name { + let mut cursor = Cursor::new(contents); + let mut captures = [Capture::EMPTY]; + while let Some(name) = cursor.find_any_ident() { + match cursor.get_text(name) { "declare_clippy_lint" => { - if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { - decl_end = Some(searcher.pos()); + if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { + decl_end = Some(cursor.pos()); } }, "impl" => { - let mut capture = ""; - if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { - match capture { + if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) { + match cursor.get_text(captures[0]) { "LateLintPass" => context = Some("LateContext"), "EarlyLintPass" => context = Some("EarlyContext"), _ => {}, diff --git a/src/tools/clippy/clippy_dev/src/parse.rs b/src/tools/clippy/clippy_dev/src/parse.rs new file mode 100644 index 0000000000000..de5caf4e1ef65 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/parse.rs @@ -0,0 +1,285 @@ +pub mod cursor; + +use self::cursor::{Capture, Cursor}; +use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; +use core::fmt::{Display, Write as _}; +use core::range::Range; +use rustc_arena::DroplessArena; +use std::fs; +use std::path::{self, Path, PathBuf}; +use std::str::pattern::Pattern; + +pub struct ParseCxImpl<'cx> { + pub arena: &'cx DroplessArena, + pub str_buf: StrBuf, +} +pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>; + +/// Calls the given function inside a newly created parsing context. +pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, ParseCxImpl<'cx>>) -> T) -> T { + let arena = DroplessArena::default(); + f(&mut Scoped::new(ParseCxImpl { + arena: &arena, + str_buf: StrBuf::with_capacity(128), + })) +} + +/// A string used as a temporary buffer used to avoid allocating for short lived strings. +pub struct StrBuf(String); +impl StrBuf { + /// Creates a new buffer with the specified initial capacity. + pub fn with_capacity(cap: usize) -> Self { + Self(String::with_capacity(cap)) + } + + /// Allocates the result of formatting the given value onto the arena. + pub fn alloc_display<'cx>(&mut self, arena: &'cx DroplessArena, value: impl Display) -> &'cx str { + self.0.clear(); + write!(self.0, "{value}").expect("`Display` impl returned an error"); + arena.alloc_str(&self.0) + } + + /// Allocates the string onto the arena with all ascii characters converted to + /// lowercase. + pub fn alloc_ascii_lower<'cx>(&mut self, arena: &'cx DroplessArena, s: &str) -> &'cx str { + self.0.clear(); + self.0.push_str(s); + self.0.make_ascii_lowercase(); + arena.alloc_str(&self.0) + } + + /// Allocates the result of replacing all instances the pattern with the given string + /// onto the arena. + pub fn alloc_replaced<'cx>( + &mut self, + arena: &'cx DroplessArena, + s: &str, + pat: impl Pattern, + replacement: &str, + ) -> &'cx str { + let mut parts = s.split(pat); + let Some(first) = parts.next() else { + return ""; + }; + self.0.clear(); + self.0.push_str(first); + for part in parts { + self.0.push_str(replacement); + self.0.push_str(part); + } + if self.0.is_empty() { + "" + } else { + arena.alloc_str(&self.0) + } + } + + /// Performs an operation with the freshly cleared buffer. + pub fn with(&mut self, f: impl FnOnce(&mut String) -> T) -> T { + self.0.clear(); + f(&mut self.0) + } +} + +pub struct Lint<'cx> { + pub name: &'cx str, + pub group: &'cx str, + pub module: &'cx str, + pub path: PathBuf, + pub declaration_range: Range, +} + +pub struct DeprecatedLint<'cx> { + pub name: &'cx str, + pub reason: &'cx str, + pub version: &'cx str, +} + +pub struct RenamedLint<'cx> { + pub old_name: &'cx str, + pub new_name: &'cx str, + pub version: &'cx str, +} + +impl<'cx> ParseCxImpl<'cx> { + /// Finds all lint declarations (`declare_clippy_lint!`) + #[must_use] + pub fn find_lint_decls(&mut self) -> Vec> { + let mut lints = Vec::with_capacity(1000); + let mut contents = String::new(); + for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { + let e = expect_action(e, ErrAction::Read, "."); + + // Skip if this isn't a lint crate's directory. + let mut crate_path = if expect_action(e.file_type(), ErrAction::Read, ".").is_dir() + && let Ok(crate_path) = e.file_name().into_string() + && crate_path.starts_with("clippy_lints") + && crate_path != "clippy_lints_internal" + { + crate_path + } else { + continue; + }; + + crate_path.push(path::MAIN_SEPARATOR); + crate_path.push_str("src"); + for e in walk_dir_no_dot_or_target(&crate_path) { + let e = expect_action(e, ErrAction::Read, &crate_path); + if let Some(path) = e.path().to_str() + && let Some(path) = path.strip_suffix(".rs") + && let Some(path) = path.get(crate_path.len() + 1..) + { + let module = if path == "lib" { + "" + } else { + let path = path + .strip_suffix("mod") + .and_then(|x| x.strip_suffix(path::MAIN_SEPARATOR)) + .unwrap_or(path); + self.str_buf + .alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::") + }; + self.parse_clippy_lint_decls( + e.path(), + File::open_read_to_cleared_string(e.path(), &mut contents), + module, + &mut lints, + ); + } + } + } + lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + lints + } + + /// Parse a source file looking for `declare_clippy_lint` macro invocations. + fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec>) { + #[allow(clippy::enum_glob_use)] + use cursor::Pat::*; + #[rustfmt::skip] + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ /// docs + Bang, OpenBrace, AnyComment, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, + ]; + + let mut cursor = Cursor::new(contents); + let mut captures = [Capture::EMPTY; 2]; + while let Some(start) = cursor.find_ident("declare_clippy_lint") { + if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { + lints.push(Lint { + name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: start as usize..cursor.pos() as usize, + }); + } + } + } + + #[must_use] + pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec>) { + #[allow(clippy::enum_glob_use)] + use cursor::Pat::*; + #[rustfmt::skip] + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); + + let mut cursor = Cursor::new(&contents); + let mut captures = [Capture::EMPTY; 3]; + + // First instance is the macro definition. + assert!( + cursor.find_ident("declare_with_version").is_some(), + "error reading deprecated lints" + ); + + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { + while cursor.match_all(DECL_TOKENS, &mut captures) { + deprecated.push(DeprecatedLint { + name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }); + } + } else { + panic!("error reading deprecated lints"); + } + + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { + while cursor.match_all(DECL_TOKENS, &mut captures) { + renamed.push(RenamedLint { + old_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }); + } + } else { + panic!("error reading renamed lints"); + } + + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(rhs.old_name)); + (deprecated, renamed) + } + + /// Removes the line splices and surrounding quotes from a string literal + fn parse_str_lit(&mut self, s: &str) -> &'cx str { + let (s, is_raw) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), true) + } else { + (s, false) + }; + let s = s + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); + + if is_raw { + if s.is_empty() { "" } else { self.arena.alloc_str(s) } + } else { + self.str_buf.with(|buf| { + rustc_literal_escaper::unescape_str(s, &mut |_, ch| { + if let Ok(ch) = ch { + buf.push(ch); + } + }); + if buf.is_empty() { "" } else { self.arena.alloc_str(buf) } + }) + } + } + + fn parse_str_single_line(&mut self, path: &Path, s: &str) -> &'cx str { + let value = self.parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value + } +} diff --git a/src/tools/clippy/clippy_dev/src/parse/cursor.rs b/src/tools/clippy/clippy_dev/src/parse/cursor.rs new file mode 100644 index 0000000000000..6dc003f326de7 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/parse/cursor.rs @@ -0,0 +1,263 @@ +use core::slice; +use rustc_lexer::{self as lex, LiteralKind, Token, TokenKind}; + +/// A token pattern used for searching and matching by the [`Cursor`]. +/// +/// In the event that a pattern is a multi-token sequence, earlier tokens will be consumed +/// even if the pattern ultimately isn't matched. e.g. With the sequence `:*` matching +/// `DoubleColon` will consume the first `:` and then fail to match, leaving the cursor at +/// the `*`. +#[derive(Clone, Copy)] +pub enum Pat<'a> { + /// Matches any number of comments and doc comments. + AnyComment, + Ident(&'a str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + Comma, + DoubleColon, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, + Semi, +} + +#[derive(Clone, Copy)] +pub struct Capture { + pub pos: u32, + pub len: u32, +} +impl Capture { + pub const EMPTY: Self = Self { pos: 0, len: 0 }; +} + +/// A unidirectional cursor over a token stream that is lexed on demand. +pub struct Cursor<'txt> { + next_token: Token, + pos: u32, + inner: lex::Cursor<'txt>, + text: &'txt str, +} +impl<'txt> Cursor<'txt> { + #[must_use] + pub fn new(text: &'txt str) -> Self { + let mut inner = lex::Cursor::new(text, lex::FrontmatterAllowed::Yes); + Self { + next_token: inner.advance_token(), + pos: 0, + inner, + text, + } + } + + /// Gets the text of the captured token assuming it came from this cursor. + #[must_use] + pub fn get_text(&self, capture: Capture) -> &'txt str { + &self.text[capture.pos as usize..(capture.pos + capture.len) as usize] + } + + /// Gets the text that makes up the next token in the stream, or the empty string if + /// stream is exhausted. + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + /// Gets the length of the next token in bytes, or zero if the stream is exhausted. + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + + /// Gets the next token in the stream, or [`TokenKind::Eof`] if the stream is + /// exhausted. + #[must_use] + pub fn peek(&self) -> TokenKind { + self.next_token.kind + } + + /// Gets the offset of the next token in the source string, or the string's length if + /// the stream is exhausted. + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + /// Gets whether the cursor has exhausted its input. + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == TokenKind::Eof + } + + /// Advances the cursor to the next token. If the stream is exhausted this will set + /// the next token to [`TokenKind::Eof`]. + pub fn step(&mut self) { + // `next_token.len` is zero for the eof marker. + self.pos += self.next_token.len; + self.next_token = self.inner.advance_token(); + } + + /// Consumes tokens until the given pattern is either fully matched of fails to match. + /// Returns whether the pattern was fully matched. + /// + /// For each capture made by the pattern one item will be taken from the capture + /// sequence with the result placed inside. + fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool { + loop { + match (pat, self.next_token.kind) { + #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/6697 + (_, TokenKind::Whitespace) + | ( + Pat::AnyComment, + TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. }, + ) => self.step(), + (Pat::AnyComment, _) => return true, + (Pat::Bang, TokenKind::Bang) + | (Pat::CloseBrace, TokenKind::CloseBrace) + | (Pat::CloseBracket, TokenKind::CloseBracket) + | (Pat::CloseParen, TokenKind::CloseParen) + | (Pat::Comma, TokenKind::Comma) + | (Pat::Eq, TokenKind::Eq) + | (Pat::Lifetime, TokenKind::Lifetime { .. }) + | (Pat::Lt, TokenKind::Lt) + | (Pat::Gt, TokenKind::Gt) + | (Pat::OpenBrace, TokenKind::OpenBrace) + | (Pat::OpenBracket, TokenKind::OpenBracket) + | (Pat::OpenParen, TokenKind::OpenParen) + | (Pat::Pound, TokenKind::Pound) + | (Pat::Semi, TokenKind::Semi) + | ( + Pat::LitStr, + TokenKind::Literal { + kind: LiteralKind::Str { terminated: true } | LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Pat::Ident(x), TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Pat::DoubleColon, TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + #[rustfmt::skip] + ( + Pat::CaptureLitStr, + TokenKind::Literal { + kind: + LiteralKind::Str { terminated: true } + | LiteralKind::RawStr { n_hashes: Some(_) }, + .. + }, + ) + | (Pat::CaptureIdent, TokenKind::Ident) => { + *captures.next().unwrap() = Capture { pos: self.pos, len: self.next_token.len }; + self.step(); + return true; + }, + _ => return false, + } + } + } + + /// Consumes all tokens until the specified identifier is found and returns its + /// position. Returns `None` if the identifier could not be found. + /// + /// The cursor will be positioned immediately after the identifier, or at the end if + /// it is not. + pub fn find_ident(&mut self, ident: &str) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident if self.peek_text() == ident => { + let pos = self.pos; + self.step(); + return Some(pos); + }, + TokenKind::Eof => return None, + _ => self.step(), + } + } + } + + /// Consumes all tokens until the next identifier is found and captures it. Returns + /// `None` if no identifier could be found. + /// + /// The cursor will be positioned immediately after the identifier, or at the end if + /// it is not. + pub fn find_any_ident(&mut self) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident => { + let res = Capture { + pos: self.pos, + len: self.next_token.len, + }; + self.step(); + return Some(res); + }, + TokenKind::Eof => return None, + _ => self.step(), + } + } + } + + /// Continually attempt to match the pattern on subsequent tokens until a match is + /// found. Returns whether the pattern was successfully matched. + /// + /// Not generally suitable for multi-token patterns or patterns that can match + /// nothing. + #[must_use] + pub fn find_pat(&mut self, pat: Pat<'_>) -> bool { + let mut capture = [].iter_mut(); + while !self.match_impl(pat, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } + + /// Attempts to match a sequence of patterns at the current position. Returns whether + /// all patterns were successfully matched. + /// + /// Captures will be written to the given slice in the order they're matched. If a + /// capture is matched, but there are no more capture slots this will panic. If the + /// match is completed without filling all the capture slots they will be left + /// unmodified. + /// + /// If the match fails the cursor will be positioned at the first failing token. + #[must_use] + pub fn match_all(&mut self, pats: &[Pat<'_>], captures: &mut [Capture]) -> bool { + let mut captures = captures.iter_mut(); + pats.iter().all(|&p| self.match_impl(p, &mut captures)) + } + + /// Attempts to match a single pattern at the current position. Returns whether the + /// pattern was successfully matched. + /// + /// If the pattern attempts to capture anything this will panic. If the match fails + /// the cursor will be positioned at the first failing token. + #[must_use] + pub fn match_pat(&mut self, pat: Pat<'_>) -> bool { + self.match_impl(pat, &mut [].iter_mut()) + } +} diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs index 15392dd1d2927..d11070bab85bf 100644 --- a/src/tools/clippy/clippy_dev/src/release.rs +++ b/src/tools/clippy/clippy_dev/src/release.rs @@ -23,7 +23,7 @@ pub fn bump_version(mut version: Version) { dst.push_str(&src[..package.version_range.start]); write!(dst, "\"{}\"", version.toml_display()).unwrap(); dst.push_str(&src[package.version_range.end..]); - UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range)) + UpdateStatus::from_changed(src.get(package.version_range) != dst.get(package.version_range)) } }); } diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs index d62597428e210..8e30eb7ce95b3 100644 --- a/src/tools/clippy/clippy_dev/src/rename_lint.rs +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -1,7 +1,9 @@ -use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::parse::cursor::{self, Capture, Cursor}; +use crate::parse::{ParseCx, RenamedLint}; +use crate::update_lints::generate_lint_files; use crate::utils::{ - ErrAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, - delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, + ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; use rustc_lexer::TokenKind; use std::ffi::OsString; @@ -24,36 +26,35 @@ use std::path::Path; /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. #[expect(clippy::too_many_lines)] -pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { - if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{old_name}` should not contain the `{prefix}` prefix"); - } - if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{new_name}` should not contain the `{prefix}` prefix"); - } - +pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str, uplift: bool) { let mut updater = FileUpdater::default(); - let mut lints = find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); + let mut lints = cx.find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { panic!("could not find lint `{old_name}`"); }; let lint = &lints[lint_idx]; - let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let old_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }); let new_name_prefixed = if uplift { - new_name.to_owned() + new_name } else { - String::from_iter(["clippy::", new_name]) + cx.str_buf.with(|buf| { + buf.extend(["clippy::", new_name]); + cx.arena.alloc_str(buf) + }) }; for lint in &mut renamed_lints { if lint.new_name == old_name_prefixed { - lint.new_name.clone_from(&new_name_prefixed); + lint.new_name = new_name_prefixed; } } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { Ok(_) => { println!("`{old_name}` already has a rename registered"); return; @@ -63,12 +64,8 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b idx, RenamedLint { old_name: old_name_prefixed, - new_name: if uplift { - new_name.to_owned() - } else { - String::from_iter(["clippy::", new_name]) - }, - version: clippy_version.rust_display().to_string(), + new_name: new_name_prefixed, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }, ); }, @@ -104,7 +101,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b } delete_test_files(old_name, change_prefixed_tests); lints.remove(lint_idx); - } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + } else if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { let lint = &mut lints[lint_idx]; if lint.module.ends_with(old_name) && lint @@ -118,13 +115,15 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b mod_edit = ModEdit::Rename; } - let mod_len = lint.module.len(); - lint.module.truncate(mod_len - old_name.len()); - lint.module.push_str(new_name); + lint.module = cx.str_buf.with(|buf| { + buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + buf.push_str(new_name); + cx.arena.alloc_str(buf) + }); } rename_test_files(old_name, new_name, change_prefixed_tests); - new_name.clone_into(&mut lints[lint_idx].name); - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints[lint_idx].name = new_name; + lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); } else { println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); @@ -132,7 +131,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b } let mut update_fn = file_update_fn(old_name, new_name, mod_edit); - for e in walk_dir_no_dot_or_target() { + for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { updater.update_file(e.path(), &mut update_fn); @@ -285,47 +284,46 @@ fn file_update_fn<'a, 'b>( move |_, src, dst| { let mut copy_pos = 0u32; let mut changed = false; - let mut searcher = RustSearcher::new(src); - let mut capture = ""; + let mut cursor = Cursor::new(src); + let mut captures = [Capture::EMPTY]; loop { - match searcher.peek() { + match cursor.peek() { TokenKind::Eof => break, TokenKind::Ident => { - let match_start = searcher.pos(); - let text = searcher.peek_text(); - searcher.step(); + let match_start = cursor.pos(); + let text = cursor.peek_text(); + cursor.step(); match text { // clippy::line_name or clippy::lint-name "clippy" => { - if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) - && capture == old_name + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; } }, // mod lint_name "mod" => { if !matches!(mod_edit, ModEdit::None) - && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) - && capture == old_name + && let Some(pos) = cursor.find_ident(old_name) { match mod_edit { ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..pos as usize]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, - ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => { let mut start = &src[copy_pos as usize..match_start as usize]; if start.ends_with("\n\n") { start = &start[..start.len() - 1]; } dst.push_str(start); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); if src[copy_pos as usize..].starts_with("\n\n") { copy_pos += 1; } @@ -337,8 +335,8 @@ fn file_update_fn<'a, 'b>( }, // lint_name:: name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { - let name_end = searcher.pos(); - if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + let name_end = cursor.pos(); + if cursor.match_pat(cursor::Pat::DoubleColon) { dst.push_str(&src[copy_pos as usize..match_start as usize]); dst.push_str(new_name); copy_pos = name_end; @@ -356,36 +354,36 @@ fn file_update_fn<'a, 'b>( }; dst.push_str(&src[copy_pos as usize..match_start as usize]); dst.push_str(replacement); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, } }, // //~ lint_name TokenKind::LineComment { doc_style: None } => { - let text = searcher.peek_text(); + let text = cursor.peek_text(); if text.starts_with("//~") && let Some(text) = text.strip_suffix(old_name) && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize + text.len()]); dst.push_str(new_name); - copy_pos = searcher.pos() + searcher.peek_len(); + copy_pos = cursor.pos() + cursor.peek_len(); changed = true; } - searcher.step(); + cursor.step(); }, // ::lint_name TokenKind::Colon - if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) - && capture == old_name => + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name => { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, - _ => searcher.step(), + _ => cursor.step(), } } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 5f6e874ffe254..3d0da68461144 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,13 +1,10 @@ -use crate::utils::{ - ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn, -}; +use crate::parse::cursor::Cursor; +use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; use std::fmt::Write; -use std::fs; -use std::ops::Range; -use std::path::{self, Path, PathBuf}; -use walkdir::{DirEntry, WalkDir}; +use std::path::{self, Path}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ // Use that command to update this file and do not edit by hand.\n\ @@ -24,18 +21,18 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// # Panics /// /// Panics if a file path could not read from or then written to -pub fn update(update_mode: UpdateMode) { - let lints = find_lint_decls(); - let (deprecated, renamed) = read_deprecated_lints(); +pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { + let lints = cx.find_lint_decls(); + let (deprecated, renamed) = cx.read_deprecated_lints(); generate_lint_files(update_mode, &lints, &deprecated, &renamed); } #[expect(clippy::too_many_lines)] pub fn generate_lint_files( update_mode: UpdateMode, - lints: &[Lint], - deprecated: &[DeprecatedLint], - renamed: &[RenamedLint], + lints: &[Lint<'_>], + deprecated: &[DeprecatedLint<'_>], + renamed: &[RenamedLint<'_>], ) { let mut updater = FileUpdater::default(); updater.update_file_checked( @@ -64,7 +61,7 @@ pub fn generate_lint_files( |dst| { for lint in lints .iter() - .map(|l| &*l.name) + .map(|l| l.name) .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) .sorted() @@ -79,13 +76,13 @@ pub fn generate_lint_files( update_mode, "clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { - let mut searcher = RustSearcher::new(src); + let mut cursor = Cursor::new(src); assert!( - searcher.find_token(Token::Ident("declare_with_version")) - && searcher.find_token(Token::Ident("declare_with_version")), + cursor.find_ident("declare_with_version").is_some() + && cursor.find_ident("declare_with_version").is_some(), "error reading deprecated lints" ); - dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str(&src[..cursor.pos() as usize]); dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); for lint in deprecated { write!( @@ -135,13 +132,13 @@ pub fn generate_lint_files( dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); for lint in renamed { - if seen_lints.insert(&lint.new_name) { + if seen_lints.insert(lint.new_name) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } seen_lints.clear(); for lint in renamed { - if seen_lints.insert(&lint.old_name) { + if seen_lints.insert(lint.old_name) { writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } } @@ -167,7 +164,7 @@ pub fn generate_lint_files( for lint_mod in lints .iter() .filter(|l| !l.module.is_empty()) - .map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0)) + .map(|l| l.module.split_once("::").map_or(l.module, |x| x.0)) .sorted() .dedup() { @@ -200,260 +197,3 @@ pub fn generate_lint_files( fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } - -/// Lint data parsed from the Clippy source code. -#[derive(PartialEq, Eq, Debug)] -pub struct Lint { - pub name: String, - pub group: String, - pub module: String, - pub path: PathBuf, - pub declaration_range: Range, -} - -pub struct DeprecatedLint { - pub name: String, - pub reason: String, - pub version: String, -} - -pub struct RenamedLint { - pub old_name: String, - pub new_name: String, - pub version: String, -} - -/// Finds all lint declarations (`declare_clippy_lint!`) -#[must_use] -pub fn find_lint_decls() -> Vec { - let mut lints = Vec::with_capacity(1000); - let mut contents = String::new(); - for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { - let e = expect_action(e, ErrAction::Read, "."); - if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { - continue; - } - let Ok(mut name) = e.file_name().into_string() else { - continue; - }; - if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { - name.push_str("/src"); - for (file, module) in read_src_with_module(name.as_ref()) { - parse_clippy_lint_decls( - file.path(), - File::open_read_to_cleared_string(file.path(), &mut contents), - &module, - &mut lints, - ); - } - } - } - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - lints -} - -/// Reads the source files from the given root directory -fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator { - WalkDir::new(src_root).into_iter().filter_map(move |e| { - let e = expect_action(e, ErrAction::Read, src_root); - let path = e.path().as_os_str().as_encoded_bytes(); - if let Some(path) = path.strip_suffix(b".rs") - && let Some(path) = path.get(src_root.as_os_str().len() + 1..) - { - if path == b"lib" { - Some((e, String::new())) - } else { - let path = if let Some(path) = path.strip_suffix(b"mod") - && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) - { - path - } else { - path - }; - if let Ok(path) = str::from_utf8(path) { - let path = path.replace(['/', '\\'], "::"); - Some((e, path)) - } else { - None - } - } - } else { - None - } - }) -} - -/// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { - #[allow(clippy::enum_glob_use)] - use Token::*; - #[rustfmt::skip] - static DECL_TOKENS: &[Token<'_>] = &[ - // !{ /// docs - Bang, OpenBrace, AnyComment, - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, - // pub NAME, GROUP, - Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, - ]; - - let mut searcher = RustSearcher::new(contents); - while searcher.find_token(Ident("declare_clippy_lint")) { - let start = searcher.pos() as usize - "declare_clippy_lint".len(); - let (mut name, mut group) = ("", ""); - if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { - lints.push(Lint { - name: name.to_lowercase(), - group: group.into(), - module: module.into(), - path: path.into(), - declaration_range: start..searcher.pos() as usize, - }); - } - } -} - -#[must_use] -pub fn read_deprecated_lints() -> (Vec, Vec) { - #[allow(clippy::enum_glob_use)] - use Token::*; - #[rustfmt::skip] - static DECL_TOKENS: &[Token<'_>] = &[ - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, - // ("first", "second"), - OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, - ]; - #[rustfmt::skip] - static DEPRECATED_TOKENS: &[Token<'_>] = &[ - // !{ DEPRECATED(DEPRECATED_VERSION) = [ - Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, - ]; - #[rustfmt::skip] - static RENAMED_TOKENS: &[Token<'_>] = &[ - // !{ RENAMED(RENAMED_VERSION) = [ - Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, - ]; - - let path = "clippy_lints/src/deprecated_lints.rs"; - let mut deprecated = Vec::with_capacity(30); - let mut renamed = Vec::with_capacity(80); - let mut contents = String::new(); - File::open_read_to_cleared_string(path, &mut contents); - - let mut searcher = RustSearcher::new(&contents); - - // First instance is the macro definition. - assert!( - searcher.find_token(Ident("declare_with_version")), - "error reading deprecated lints" - ); - - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { - let mut version = ""; - let mut name = ""; - let mut reason = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { - deprecated.push(DeprecatedLint { - name: parse_str_single_line(path.as_ref(), name), - reason: parse_str_single_line(path.as_ref(), reason), - version: parse_str_single_line(path.as_ref(), version), - }); - } - } else { - panic!("error reading deprecated lints"); - } - - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { - let mut version = ""; - let mut old_name = ""; - let mut new_name = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { - renamed.push(RenamedLint { - old_name: parse_str_single_line(path.as_ref(), old_name), - new_name: parse_str_single_line(path.as_ref(), new_name), - version: parse_str_single_line(path.as_ref(), version), - }); - } - } else { - panic!("error reading renamed lints"); - } - - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); - (deprecated, renamed) -} - -/// Removes the line splices and surrounding quotes from a string literal -fn parse_str_lit(s: &str) -> String { - let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#'); - let s = s - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) - .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); - let mut res = String::with_capacity(s.len()); - rustc_literal_escaper::unescape_str(s, &mut |_, ch| { - if let Ok(ch) = ch { - res.push(ch); - } - }); - res -} - -fn parse_str_single_line(path: &Path, s: &str) -> String { - let value = parse_str_lit(s); - assert!( - !value.contains('\n'), - "error parsing `{}`: `{s}` should be a single line string", - path.display(), - ); - value -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_clippy_lint_decls() { - static CONTENTS: &str = r#" - declare_clippy_lint! { - #[clippy::version = "Hello Clippy!"] - pub PTR_ARG, - style, - "really long \ - text" - } - - declare_clippy_lint!{ - #[clippy::version = "Test version"] - pub DOC_MARKDOWN, - pedantic, - "single line" - } - "#; - let mut result = Vec::new(); - parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); - for r in &mut result { - r.declaration_range = Range::default(); - } - - let expected = vec![ - Lint { - name: "ptr_arg".into(), - group: "style".into(), - module: "module_name".into(), - path: PathBuf::new(), - declaration_range: Range::default(), - }, - Lint { - name: "doc_markdown".into(), - group: "pedantic".into(), - module: "module_name".into(), - path: PathBuf::new(), - declaration_range: Range::default(), - }, - ]; - assert_eq!(expected, result); - } -} diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index 057951d0e33bd..52452dd86b490 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,9 +1,9 @@ use core::fmt::{self, Display}; +use core::marker::PhantomData; use core::num::NonZero; -use core::ops::Range; -use core::slice; +use core::ops::{Deref, DerefMut}; +use core::range::Range; use core::str::FromStr; -use rustc_lexer::{self as lexer, FrontmatterAllowed}; use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; @@ -12,6 +12,24 @@ use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; +pub struct Scoped<'inner, 'outer: 'inner, T>(T, PhantomData<&'inner mut T>, PhantomData<&'outer mut ()>); +impl Scoped<'_, '_, T> { + pub fn new(value: T) -> Self { + Self(value, PhantomData, PhantomData) + } +} +impl Deref for Scoped<'_, '_, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Scoped<'_, '_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[derive(Clone, Copy)] pub enum ErrAction { Open, @@ -410,179 +428,6 @@ pub fn update_text_region_fn( move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } -#[derive(Clone, Copy)] -pub enum Token<'a> { - /// Matches any number of comments / doc comments. - AnyComment, - Ident(&'a str), - CaptureIdent, - LitStr, - CaptureLitStr, - Bang, - CloseBrace, - CloseBracket, - CloseParen, - /// This will consume the first colon even if the second doesn't exist. - DoubleColon, - Comma, - Eq, - Lifetime, - Lt, - Gt, - OpenBrace, - OpenBracket, - OpenParen, - Pound, - Semi, -} - -pub struct RustSearcher<'txt> { - text: &'txt str, - cursor: lexer::Cursor<'txt>, - pos: u32, - next_token: lexer::Token, -} -impl<'txt> RustSearcher<'txt> { - #[must_use] - #[expect(clippy::inconsistent_struct_constructor)] - pub fn new(text: &'txt str) -> Self { - let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); - Self { - text, - pos: 0, - next_token: cursor.advance_token(), - cursor, - } - } - - #[must_use] - pub fn peek_text(&self) -> &'txt str { - &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] - } - - #[must_use] - pub fn peek_len(&self) -> u32 { - self.next_token.len - } - - #[must_use] - pub fn peek(&self) -> lexer::TokenKind { - self.next_token.kind - } - - #[must_use] - pub fn pos(&self) -> u32 { - self.pos - } - - #[must_use] - pub fn at_end(&self) -> bool { - self.next_token.kind == lexer::TokenKind::Eof - } - - pub fn step(&mut self) { - // `next_len` is zero for the sentinel value and the eof marker. - self.pos += self.next_token.len; - self.next_token = self.cursor.advance_token(); - } - - /// Consumes the next token if it matches the requested value and captures the value if - /// requested. Returns true if a token was matched. - fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { - loop { - match (token, self.next_token.kind) { - (_, lexer::TokenKind::Whitespace) - | ( - Token::AnyComment, - lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, - ) => self.step(), - (Token::AnyComment, _) => return true, - (Token::Bang, lexer::TokenKind::Bang) - | (Token::CloseBrace, lexer::TokenKind::CloseBrace) - | (Token::CloseBracket, lexer::TokenKind::CloseBracket) - | (Token::CloseParen, lexer::TokenKind::CloseParen) - | (Token::Comma, lexer::TokenKind::Comma) - | (Token::Eq, lexer::TokenKind::Eq) - | (Token::Lifetime, lexer::TokenKind::Lifetime { .. }) - | (Token::Lt, lexer::TokenKind::Lt) - | (Token::Gt, lexer::TokenKind::Gt) - | (Token::OpenBrace, lexer::TokenKind::OpenBrace) - | (Token::OpenBracket, lexer::TokenKind::OpenBracket) - | (Token::OpenParen, lexer::TokenKind::OpenParen) - | (Token::Pound, lexer::TokenKind::Pound) - | (Token::Semi, lexer::TokenKind::Semi) - | ( - Token::LitStr, - lexer::TokenKind::Literal { - kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, - .. - }, - ) => { - self.step(); - return true; - }, - (Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => { - self.step(); - return true; - }, - (Token::DoubleColon, lexer::TokenKind::Colon) => { - self.step(); - if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) { - self.step(); - return true; - } - return false; - }, - ( - Token::CaptureLitStr, - lexer::TokenKind::Literal { - kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, - .. - }, - ) - | (Token::CaptureIdent, lexer::TokenKind::Ident) => { - **captures.next().unwrap() = self.peek_text(); - self.step(); - return true; - }, - _ => return false, - } - } - } - - #[must_use] - pub fn find_token(&mut self, token: Token<'_>) -> bool { - let mut capture = [].iter_mut(); - while !self.read_token(token, &mut capture) { - self.step(); - if self.at_end() { - return false; - } - } - true - } - - #[must_use] - pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> { - let mut res = ""; - let mut capture = &mut res; - let mut capture = slice::from_mut(&mut capture).iter_mut(); - while !self.read_token(token, &mut capture) { - self.step(); - if self.at_end() { - return None; - } - } - Some(res) - } - - #[must_use] - pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool { - let mut captures = captures.iter_mut(); - tokens.iter().all(|&t| self.read_token(t, &mut captures)) - } -} - #[track_caller] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { @@ -748,8 +593,8 @@ pub fn delete_dir_if_exists(path: &Path) { } /// Walks all items excluding top-level dot files/directories and any target directories. -pub fn walk_dir_no_dot_or_target() -> impl Iterator> { - WalkDir::new(".").into_iter().filter_entry(|e| { +pub fn walk_dir_no_dot_or_target(p: impl AsRef) -> impl Iterator> { + WalkDir::new(p).into_iter().filter_entry(|e| { e.path() .file_name() .is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.')) diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 42486e182ee34..bc97746a1cbae 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.92" +version = "0.1.93" 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/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 375d179681da3..a754eea311651 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -135,7 +135,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::duplicate_mod::DUPLICATE_MOD_INFO, crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, crate::empty_drop::EMPTY_DROP_INFO, - crate::empty_enum::EMPTY_ENUM_INFO, + crate::empty_enums::EMPTY_ENUMS_INFO, crate::empty_line_after::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::empty_line_after::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO, @@ -227,8 +227,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, - crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, - crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO, @@ -258,7 +256,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, - crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, @@ -298,7 +295,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, - crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, @@ -404,6 +400,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, + crate::methods::LINES_FILTER_MAP_OK_INFO, crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, @@ -545,7 +542,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, - crate::needless_if::NEEDLESS_IF_INFO, + crate::needless_ifs::NEEDLESS_IFS_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, @@ -592,6 +589,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, + crate::operators::INTEGER_DIVISION_REMAINDER_USED_INFO, + crate::operators::INVALID_UPCAST_COMPARISONS_INFO, + crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, crate::operators::MANUAL_MIDPOINT_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index f087a894d76ae..f010d17917f9e 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -85,6 +85,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::drop_copy", "dropping_copy_types"), #[clippy::version = ""] ("clippy::drop_ref", "dropping_references"), + #[clippy::version = "1.92.0"] + ("clippy::empty_enum", "clippy::empty_enums"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), #[clippy::version = "1.53.0"] @@ -137,6 +139,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), #[clippy::version = "1.80.0"] ("clippy::mismatched_target_os", "unexpected_cfgs"), + #[clippy::version = "1.92.0"] + ("clippy::needless_if", "clippy::needless_ifs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 2a3fb82946117..1e1d6e69cc91c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -22,7 +22,6 @@ use rustc_resolve::rustdoc::{ }; use rustc_session::impl_lint_pass; use rustc_span::Span; -use rustc_span::edition::Edition; use std::ops::Range; use url::Url; @@ -36,6 +35,7 @@ mod markdown; mod missing_headers; mod needless_doctest_main; mod suspicious_doc_comments; +mod test_attr_in_doctest; mod too_long_first_doc_paragraph; declare_clippy_lint! { @@ -900,8 +900,6 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ )) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; - enum Container { Blockquote, List(usize), @@ -966,6 +964,70 @@ fn check_for_code_clusters<'a, Events: Iterator Self { + Self { + no_run: false, + ignore: false, + compile_fail: false, + + rust: true, + } + } +} + +impl CodeTags { + /// Based on + fn parse(lang: &str) -> Self { + let mut tags = Self::default(); + + let mut seen_rust_tags = false; + let mut seen_other_tags = false; + for item in lang.split([',', ' ', '\t']) { + match item.trim() { + "" => {}, + "rust" => { + tags.rust = true; + seen_rust_tags = true; + }, + "ignore" => { + tags.ignore = true; + seen_rust_tags = !seen_other_tags; + }, + "no_run" => { + tags.no_run = true; + seen_rust_tags = !seen_other_tags; + }, + "should_panic" => seen_rust_tags = !seen_other_tags, + "compile_fail" => { + tags.compile_fail = true; + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + "test_harness" | "standalone_crate" => { + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + _ if item.starts_with("ignore-") => seen_rust_tags = true, + _ if item.starts_with("edition") => {}, + _ => seen_other_tags = true, + } + } + + tags.rust &= seen_rust_tags || !seen_other_tags; + + tags + } +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -981,14 +1043,10 @@ fn check_doc<'a, Events: Iterator, Range DocHeaders { // true if a safety header was found let mut headers = DocHeaders::default(); - let mut in_code = false; + let mut code = None; let mut in_link = None; let mut in_heading = false; let mut in_footnote_definition = false; - let mut is_rust = false; - let mut no_test = false; - let mut ignore = false; - let mut edition = None; let mut ticks_unbalanced = false; let mut text_to_check: Vec<(CowStr<'_>, Range, isize)> = Vec::new(); let mut paragraph_range = 0..0; @@ -1048,31 +1106,12 @@ fn check_doc<'a, Events: Iterator, Range { - in_code = true; - if let CodeBlockKind::Fenced(lang) = kind { - for item in lang.split(',') { - if item == "ignore" { - is_rust = false; - break; - } else if item == "no_test" { - no_test = true; - } else if item == "no_run" || item == "compile_fail" { - ignore = true; - } - if let Some(stripped) = item.strip_prefix("edition") { - is_rust = true; - edition = stripped.parse::().ok(); - } else if item.is_empty() || RUST_CODE.contains(&item) { - is_rust = true; - } - } - } - }, - End(TagEnd::CodeBlock) => { - in_code = false; - is_rust = false; - ignore = false; + code = Some(match kind { + CodeBlockKind::Indented => CodeTags::default(), + CodeBlockKind::Fenced(lang) => CodeTags::parse(lang), + }); }, + End(TagEnd::CodeBlock) => code = None, Start(Link { dest_url, .. }) => in_link = Some(dest_url), End(TagEnd::Link) => in_link = None, Start(Heading { .. } | Paragraph | Item) => { @@ -1182,7 +1221,7 @@ fn check_doc<'a, Events: Iterator, Range, Range>) { - test_attr_spans.extend( - item.attrs - .iter() - .find(|attr| attr.has_name(sym::test)) - .map(|attr| attr.span.lo().to_usize()..ident.span.hi().to_usize()), - ); -} - -pub fn check( - cx: &LateContext<'_>, - text: &str, - edition: Edition, - range: Range, - fragments: Fragments<'_>, - ignore: bool, -) { - // return whether the code contains a needless `fn main` plus a vector of byte position ranges - // of all `#[test]` attributes in not ignored code examples - fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec>) { - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, &[], None, || { - let mut test_attr_spans = vec![]; - let filename = FileName::anon_source_code(&code); - - let translator = rustc_driver::default_translator(); - let emitter = HumanEmitter::new(AutoStream::never(Box::new(io::sink())), translator); - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx - let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let psess = ParseSess::with_dcx(dcx, sm); - - let mut parser = - match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; - - let mut relevant_main_found = false; - let mut eligible = true; - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => match &item.kind { - ItemKind::Fn(box Fn { - ident, - sig, - body: Some(block), - .. - }) if ident.name == sym::main => { - if !ignore { - get_test_spans(&item, *ident, &mut test_attr_spans); - } - - let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - FnRetTy::Ty(_) => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - eligible = false; - } - }, - // Another function was found; this case is ignored for needless_doctest_main - ItemKind::Fn(fn_) => { - eligible = false; - if ignore { - // If ignore is active invalidating one lint, - // and we already found another function thus - // invalidating the other one, we have no - // business continuing. - return (false, test_attr_spans); - } - get_test_spans(&item, fn_.ident, &mut test_attr_spans); - }, - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => { - eligible = false; - }, - _ => {}, - }, - Ok(None) => break, - Err(e) => { - // See issue #15041. When calling `.cancel()` on the `Diag`, Clippy will unexpectedly panic - // when the `Diag` is unwinded. Meanwhile, we can just call `.emit()`, since the `DiagCtxt` - // is just a sink, nothing will be printed. - e.emit(); - return (false, test_attr_spans); - }, - } - } - - (relevant_main_found & eligible, test_attr_spans) - }) - }) - .ok() - .unwrap_or_default() +use rustc_span::InnerSpan; + +fn returns_unit<'a>(mut tokens: impl Iterator) -> bool { + let mut next = || tokens.next().map_or(TokenKind::Whitespace, |(kind, ..)| kind); + + match next() { + // { + TokenKind::OpenBrace => true, + // - > ( ) { + TokenKind::Minus => { + next() == TokenKind::Gt + && next() == TokenKind::OpenParen + && next() == TokenKind::CloseParen + && next() == TokenKind::OpenBrace + }, + _ => false, } +} - let trailing_whitespace = text.len() - text.trim_end().len(); - - // We currently only test for "fn main". Checking for the real - // entrypoint (with tcx.entry_fn(())) in each block would be unnecessarily - // expensive, as those are probably intended and relevant. Same goes for - // macros and other weird ways of declaring a main function. - // - // Also, as we only check for attribute names and don't do macro expansion, - // we can check only for #[test] - - if !((text.contains("main") && text.contains("fn")) || text.contains("#[test]")) { +pub fn check(cx: &LateContext<'_>, text: &str, offset: usize, fragments: Fragments<'_>) { + if !text.contains("main") { return; } - // Because of the global session, we need to create a new session in a different thread with - // the edition we need. - let text = text.to_owned(); - let (has_main, test_attr_spans) = thread::spawn(move || check_code_sample(text, edition, ignore)) - .join() - .expect("thread::spawn failed"); - if has_main && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) { - span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); - } - for span in test_attr_spans { - let span = (range.start + span.start)..(range.start + span.end); - if let Some(span) = fragments.span(cx, span) { - span_lint(cx, TEST_ATTR_IN_DOCTEST, span, "unit tests in doctest are not executed"); + let mut tokens = tokenize_with_text(text).filter(|&(kind, ..)| { + !matches!( + kind, + TokenKind::Whitespace | TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } + ) + }); + if let Some((TokenKind::Ident, "fn", fn_span)) = tokens.next() + && let Some((TokenKind::Ident, "main", main_span)) = tokens.next() + && let Some((TokenKind::OpenParen, ..)) = tokens.next() + && let Some((TokenKind::CloseParen, ..)) = tokens.next() + && returns_unit(&mut tokens) + { + let mut depth = 1; + for (kind, ..) in &mut tokens { + match kind { + TokenKind::OpenBrace => depth += 1, + TokenKind::CloseBrace => { + depth -= 1; + if depth == 0 { + break; + } + }, + _ => {}, + } + } + + if tokens.next().is_none() + && let Some(span) = fragments.span(cx, fn_span.start + offset..main_span.end + offset) + { + span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } } diff --git a/src/tools/clippy/clippy_lints/src/doc/test_attr_in_doctest.rs b/src/tools/clippy/clippy_lints/src/doc/test_attr_in_doctest.rs new file mode 100644 index 0000000000000..65738434ac286 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc/test_attr_in_doctest.rs @@ -0,0 +1,34 @@ +use super::Fragments; +use crate::doc::TEST_ATTR_IN_DOCTEST; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::tokenize_with_text; +use rustc_lexer::TokenKind; +use rustc_lint::LateContext; + +pub fn check(cx: &LateContext<'_>, text: &str, offset: usize, fragments: Fragments<'_>) { + if !text.contains("#[test]") { + return; + } + + let mut spans = Vec::new(); + let mut tokens = tokenize_with_text(text).filter(|&(kind, ..)| kind != TokenKind::Whitespace); + while let Some(token) = tokens.next() { + if let (TokenKind::Pound, _, pound_span) = token + && let Some((TokenKind::OpenBracket, ..)) = tokens.next() + && let Some((TokenKind::Ident, "test", _)) = tokens.next() + && let Some((TokenKind::CloseBracket, _, close_span)) = tokens.next() + && let Some(span) = fragments.span(cx, pound_span.start + offset..close_span.end + offset) + { + spans.push(span); + } + } + + if !spans.is_empty() { + span_lint( + cx, + TEST_ATTR_IN_DOCTEST, + spans, + "unit tests in doctest are not executed", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs index bddf4702fb340..8defbeeaa5f22 100644 --- a/src/tools/clippy/clippy_lints/src/double_parens.rs +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{HasSession, SpanRangeExt, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -47,8 +47,12 @@ impl EarlyLintPass for DoubleParens { // ^^^^^^ expr // ^^^^ inner ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { - // suggest removing the outer parens - if expr.span.eq_ctxt(inner.span) { + if expr.span.eq_ctxt(inner.span) + && !expr.span.in_external_macro(cx.sess().source_map()) + && check_source(cx, inner) + { + // suggest removing the outer parens + let mut applicability = Applicability::MachineApplicable; // We don't need to use `snippet_with_context` here, because: // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) @@ -74,8 +78,12 @@ impl EarlyLintPass for DoubleParens { if let [arg] = &**args && let ExprKind::Paren(inner) = &arg.kind => { - // suggest removing the inner parens - if expr.span.eq_ctxt(arg.span) { + if expr.span.eq_ctxt(arg.span) + && !arg.span.in_external_macro(cx.sess().source_map()) + && check_source(cx, arg) + { + // suggest removing the inner parens + let mut applicability = Applicability::MachineApplicable; let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( @@ -93,3 +101,22 @@ impl EarlyLintPass for DoubleParens { } } } + +/// Check that the span does indeed look like `( (..) )` +fn check_source(cx: &EarlyContext<'_>, inner: &Expr) -> bool { + if let Some(sfr) = inner.span.get_source_range(cx) + // this is the same as `SourceFileRange::as_str`, but doesn't apply the range right away, because + // we're interested in the source code outside it + && let Some(src) = sfr.sf.src.as_ref().map(|src| src.as_str()) + && let Some((start, outer_after_inner)) = src.split_at_checked(sfr.range.end) + && let Some((outer_before_inner, inner)) = start.split_at_checked(sfr.range.start) + && outer_before_inner.trim_end().ends_with('(') + && inner.starts_with('(') + && inner.ends_with(')') + && outer_after_inner.trim_start().starts_with(')') + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enums.rs similarity index 83% rename from src/tools/clippy/clippy_lints/src/empty_enum.rs rename to src/tools/clippy/clippy_lints/src/empty_enums.rs index b0389fd9a2f62..f96854411fe6f 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enums.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::span_contains_cfg; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -25,10 +26,6 @@ declare_clippy_lint! { /// matched to mark code unreachable. If the field is not visible, then the struct /// acts like any other struct with private fields. /// - /// * If the enum has no variants only because all variants happen to be - /// [disabled by conditional compilation][cfg], then it would be appropriate - /// to allow the lint, with `#[allow(empty_enum)]`. - /// /// For further information, visit /// [the never type’s documentation][`!`]. /// @@ -53,24 +50,24 @@ declare_clippy_lint! { /// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction /// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html #[clippy::version = "pre 1.29.0"] - pub EMPTY_ENUM, + pub EMPTY_ENUMS, pedantic, "enum with no variants" } -declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); +declare_lint_pass!(EmptyEnums => [EMPTY_ENUMS]); -impl LateLintPass<'_> for EmptyEnum { +impl LateLintPass<'_> for EmptyEnums { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Enum(..) = item.kind + if let ItemKind::Enum(.., def) = item.kind + && def.variants.is_empty() // Only suggest the `never_type` if the feature is enabled && cx.tcx.features().never_type() - && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def() - && adt.variants().is_empty() + && !span_contains_cfg(cx, item.span) { span_lint_and_help( cx, - EMPTY_ENUM, + EMPTY_ENUMS, item.span, "enum with no variants", None, diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index c0b0fd88d9e16..fb98cb30ae902 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -157,6 +157,11 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { let spans = if explicit_params.len() == extra_params.len() { vec![self.generics.span] // Remove the entire list of generics } else { + // 1. Start from the last extra param + // 2. While the params preceding it are also extra, construct spans going from the current param to + // the comma before it + // 3. Once this chain of extra params stops, switch to constructing spans going from the current + // param to the comma _after_ it let mut end: Option = None; extra_params .iter() diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index 1c751643becb6..3b9c70e23e20d 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -110,7 +110,7 @@ declare_clippy_lint! { /// } if bar { // looks like an `else` is missing here /// } /// ``` - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] pub POSSIBLE_MISSING_ELSE, suspicious, "possibly missing `else`" diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index bfe7a6355c00d..716334656926d 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::DefKind; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span, sym}; @@ -83,6 +82,10 @@ pub struct IncompatibleMsrv { availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, core_crate: Option, + + // The most recently called path. Used to skip checking the path after it's + // been checked when visiting the call expression. + called_path: Option, } impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); @@ -98,6 +101,7 @@ impl IncompatibleMsrv { .iter() .find(|krate| tcx.crate_name(**krate) == sym::core) .copied(), + called_path: None, } } @@ -140,7 +144,14 @@ impl IncompatibleMsrv { } /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV. - fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) { + fn emit_lint_if_under_msrv( + &mut self, + cx: &LateContext<'_>, + needs_const: bool, + def_id: DefId, + node: HirId, + span: Span, + ) { if def_id.is_local() { // We don't check local items since their MSRV is supposed to always be valid. return; @@ -158,10 +169,6 @@ impl IncompatibleMsrv { return; } - let needs_const = cx.enclosing_body.is_some() - && is_in_const_context(cx) - && matches!(cx.tcx.def_kind(def_id), DefKind::AssocFn | DefKind::Fn); - if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) @@ -190,13 +197,33 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { match expr.kind { ExprKind::MethodCall(_, _, _, span) => { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); + self.emit_lint_if_under_msrv(cx, is_in_const_context(cx), method_did, expr.hir_id, span); } }, - ExprKind::Path(qpath) => { - if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() { - self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, expr.span); - } + ExprKind::Call(callee, _) if let ExprKind::Path(qpath) = callee.kind => { + self.called_path = Some(callee.hir_id); + let needs_const = is_in_const_context(cx); + let def_id = if let Some(def_id) = cx.qpath_res(&qpath, callee.hir_id).opt_def_id() { + def_id + } else if needs_const && let ty::FnDef(def_id, _) = *cx.typeck_results().expr_ty(callee).kind() { + // Edge case where a function is first assigned then called. + // We previously would have warned for the non-const MSRV, when + // checking the path, but now that it's called the const MSRV + // must also be met. + def_id + } else { + return; + }; + self.emit_lint_if_under_msrv(cx, needs_const, def_id, expr.hir_id, callee.span); + }, + // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should + // not be linted as they will not be generated in older compilers if the function is not available, + // and the compiler is allowed to call unstable functions. + ExprKind::Path(qpath) + if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() + && self.called_path != Some(expr.hir_id) => + { + self.emit_lint_if_under_msrv(cx, false, path_def_id, expr.hir_id, expr.span); }, _ => {}, } @@ -208,7 +235,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { // `CStr` and `CString` have been moved around but have been available since Rust 1.0.0 && !matches!(cx.tcx.get_diagnostic_name(ty_def_id), Some(sym::cstr_type | sym::cstring_type)) { - self.emit_lint_if_under_msrv(cx, ty_def_id, hir_ty.hir_id, hir_ty.span); + self.emit_lint_if_under_msrv(cx, false, ty_def_id, hir_ty.hir_id, hir_ty.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 919702c5714ad..e95816353df61 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -94,7 +94,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap> 1; - /// ``` - #[clippy::version = "1.79.0"] - pub INTEGER_DIVISION_REMAINDER_USED, - restriction, - "use of disallowed default division and remainder operations" -} - -declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]); - -impl LateLintPass<'_> for IntegerDivisionRemainderUsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Binary(op, lhs, rhs) = &expr.kind - && let BinOpKind::Div | BinOpKind::Rem = op.node - && let lhs_ty = cx.typeck_results().expr_ty(lhs) - && let rhs_ty = cx.typeck_results().expr_ty(rhs) - && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() - && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() - { - span_lint( - cx, - INTEGER_DIVISION_REMAINDER_USED, - expr.span.source_callsite(), - format!("use of {} has been disallowed in this context", op.node.as_str()), - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 04a8e4739b853..877bd34a732bc 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,4 +1,6 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +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}; @@ -10,12 +12,12 @@ 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, TraitItemId, - TyKind, + Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, RustcVersion, + StabilityLevel, StableSince, TraitItemId, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FnSig, Ty}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::kw; use rustc_span::{Ident, Span, Symbol}; @@ -120,7 +122,17 @@ declare_clippy_lint! { "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } -declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); +pub struct LenZero { + msrv: Msrv, +} + +impl_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); + +impl LenZero { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -184,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { _ => false, } && !expr.span.from_expansion() - && has_is_empty(cx, lt.init) + && has_is_empty(cx, lt.init, self.msrv) { let mut applicability = Applicability::MachineApplicable; @@ -206,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { - check_empty_expr( + self.check_empty_expr( cx, expr.span, lhs_expr, @@ -226,29 +238,110 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { let actual_span = span_without_enclosing_paren(cx, expr.span); match cmp { BinOpKind::Eq => { - check_cmp(cx, actual_span, left, right, "", 0); // len == 0 - check_cmp(cx, actual_span, right, left, "", 0); // 0 == len + self.check_cmp(cx, actual_span, left, right, "", 0); // len == 0 + self.check_cmp(cx, actual_span, right, left, "", 0); // 0 == len }, BinOpKind::Ne => { - check_cmp(cx, actual_span, left, right, "!", 0); // len != 0 - check_cmp(cx, actual_span, right, left, "!", 0); // 0 != len + self.check_cmp(cx, actual_span, left, right, "!", 0); // len != 0 + self.check_cmp(cx, actual_span, right, left, "!", 0); // 0 != len }, BinOpKind::Gt => { - check_cmp(cx, actual_span, left, right, "!", 0); // len > 0 - check_cmp(cx, actual_span, right, left, "", 1); // 1 > len + self.check_cmp(cx, actual_span, left, right, "!", 0); // len > 0 + self.check_cmp(cx, actual_span, right, left, "", 1); // 1 > len }, BinOpKind::Lt => { - check_cmp(cx, actual_span, left, right, "", 1); // len < 1 - check_cmp(cx, actual_span, right, left, "!", 0); // 0 < len + self.check_cmp(cx, actual_span, left, right, "", 1); // len < 1 + self.check_cmp(cx, actual_span, right, left, "!", 0); // 0 < len }, - BinOpKind::Ge => check_cmp(cx, actual_span, left, right, "!", 1), // len >= 1 - BinOpKind::Le => check_cmp(cx, actual_span, right, left, "!", 1), // 1 <= len + BinOpKind::Ge => self.check_cmp(cx, actual_span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => self.check_cmp(cx, actual_span, right, left, "!", 1), // 1 <= len _ => (), } } } } +impl LenZero { + fn check_cmp( + &self, + cx: &LateContext<'_>, + span: Span, + method: &Expr<'_>, + lit: &Expr<'_>, + op: &str, + compare_to: u32, + ) { + if method.span.from_expansion() { + return; + } + + if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { + // check if we are in an is_empty() method + if parent_item_name(cx, method) == Some(sym::is_empty) { + return; + } + + self.check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); + } else { + self.check_empty_expr(cx, span, method, lit, op); + } + } + + #[expect(clippy::too_many_arguments)] + fn check_len( + &self, + cx: &LateContext<'_>, + span: Span, + method_name: Symbol, + receiver: &Expr<'_>, + lit: &LitKind, + op: &str, + compare_to: u32, + ) { + if let LitKind::Int(lit, _) = *lit { + // check if length is compared to the specified number + if lit != u128::from(compare_to) { + return; + } + + if method_name == sym::len && has_is_empty(cx, receiver, self.msrv) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + LEN_ZERO, + span, + format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), + format!("using `{op}is_empty` is clearer and more explicit"), + format!( + "{op}{}.is_empty()", + snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, + ), + applicability, + ); + } + } + } + + fn check_empty_expr(&self, cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1, self.msrv) { + let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lit1); + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); + + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + "comparison to empty slice", + format!("using `{op}is_empty` is clearer and more explicit"), + format!("{op}{lit_str}.is_empty()"), + applicability, + ); + } + } +} + fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { let Some(snippet) = span.get_source_text(cx) else { return span; @@ -513,75 +606,6 @@ fn check_for_is_empty( } } -fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if method.span.from_expansion() { - return; - } - - if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { - // check if we are in an is_empty() method - if parent_item_name(cx, method) == Some(sym::is_empty) { - return; - } - - check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); - } else { - check_empty_expr(cx, span, method, lit, op); - } -} - -fn check_len( - cx: &LateContext<'_>, - span: Span, - method_name: Symbol, - receiver: &Expr<'_>, - lit: &LitKind, - op: &str, - compare_to: u32, -) { - if let LitKind::Int(lit, _) = *lit { - // check if length is compared to the specified number - if lit != u128::from(compare_to) { - return; - } - - if method_name == sym::len && has_is_empty(cx, receiver) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - LEN_ZERO, - span, - format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - format!("using `{op}is_empty` is clearer and more explicit"), - format!( - "{op}{}.is_empty()", - snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, - ), - applicability, - ); - } - } -} - -fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { - if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { - let mut applicability = Applicability::MachineApplicable; - - let lit1 = peel_ref_operators(cx, lit1); - let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); - - span_lint_and_sugg( - cx, - COMPARISON_TO_EMPTY, - span, - "comparison to empty slice", - format!("using `{op}is_empty` is clearer and more explicit"), - format!("{op}{lit_str}.is_empty()"), - applicability, - ); - } -} - fn is_empty_string(expr: &Expr<'_>) -> bool { if let ExprKind::Lit(lit) = expr.kind && let LitKind::Str(lit, _) = lit.node @@ -600,45 +624,59 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { } /// Checks if this type has an `is_empty` method. -fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. - fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { + fn is_is_empty_and_stable(cx: &LateContext<'_>, item: &ty::AssocItem, msrv: Msrv) -> bool { if item.is_fn() { let sig = cx.tcx.fn_sig(item.def_id).skip_binder(); let ty = sig.skip_binder(); ty.inputs().len() == 1 + && cx.tcx.lookup_stability(item.def_id).is_none_or(|stability| { + if let StabilityLevel::Stable { since, .. } = stability.level { + let version = match since { + StableSince::Version(version) => version, + StableSince::Current => RustcVersion::CURRENT, + StableSince::Err(_) => return false, + }; + + msrv.meets(cx, version) + } else { + // Unstable fn, check if the feature is enabled. + cx.tcx.features().enabled(stability.feature) && msrv.current(cx).is_none() + } + }) } else { false } } /// Checks the inherent impl's items for an `is_empty(self)` method. - fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId, msrv: Msrv) -> bool { cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) .filter_by_name_unhygienic(sym::is_empty) - .any(|item| is_is_empty(cx, item)) + .any(|item| is_is_empty_and_stable(cx, item, msrv)) }) } - fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize, msrv: Msrv) -> bool { match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { cx.tcx .associated_items(principal.def_id()) .filter_by_name_unhygienic(sym::is_empty) - .any(|item| is_is_empty(cx, item)) + .any(|item| is_is_empty_and_stable(cx, item, msrv)) }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id, msrv), ty::Adt(id, _) => { - has_is_empty_impl(cx, id.did()) + has_is_empty_impl(cx, id.did(), msrv) || (cx.tcx.recursion_limit().value_within_limit(depth) && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { implements_trait(cx, ty, deref_id, &[]) && cx .get_associated_type(ty, deref_id, sym::Target) - .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1, msrv)) })) }, ty::Array(..) | ty::Slice(..) | ty::Str => true, @@ -646,5 +684,5 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } - ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0, msrv) } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a4ad9424b3eb1..99cafc7fc6d89 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -32,7 +32,6 @@ extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_data_structures; -extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; @@ -43,7 +42,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_parse; extern crate rustc_parse_format; extern crate rustc_resolve; extern crate rustc_session; @@ -117,7 +115,7 @@ mod drop_forget_ref; mod duplicate_mod; mod else_if_without_else; mod empty_drop; -mod empty_enum; +mod empty_enums; mod empty_line_after; mod empty_with_brackets; mod endian_bytes; @@ -171,8 +169,6 @@ mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; mod int_plus_one; -mod integer_division_remainder_used; -mod invalid_upcast_comparisons; mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; @@ -191,7 +187,6 @@ mod let_if_seq; mod let_underscore; mod let_with_type_underscore; mod lifetimes; -mod lines_filter_map_ok; mod literal_representation; mod literal_string_with_formatting_args; mod loops; @@ -203,7 +198,6 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_ignore_case_cmp; @@ -255,7 +249,7 @@ mod needless_borrows_for_generic_args; mod needless_continue; mod needless_else; mod needless_for_each; -mod needless_if; +mod needless_ifs; mod needless_late_init; mod needless_maybe_sized; mod needless_parens_on_range_literals; @@ -481,7 +475,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); - store.register_late_pass(|_| Box::new(len_zero::LenZero)); + store.register_late_pass(move |_| Box::new(len_zero::LenZero::new(conf))); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions)); store.register_late_pass(|_| Box::new(unicode::Unicode)); @@ -542,8 +536,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(derive::Derive)); store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); - store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); - store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); + store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); @@ -743,7 +736,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); - store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf))); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); @@ -755,7 +747,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes)); store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); - store.register_late_pass(|_| Box::new(needless_if::NeedlessIf)); + store.register_late_pass(|_| Box::new(needless_ifs::NeedlessIfs)); store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); @@ -798,7 +790,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); @@ -807,7 +798,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); - store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 519ec228c8840..727e9b172a872 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -856,89 +856,49 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - if !elidable_lts - .iter() - .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) - { - return None; - } - - let mut suggestions = if elidable_lts.is_empty() { - vec![] - } else if elidable_lts.len() == explicit_params.len() { + let mut suggestions = if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - match &explicit_params[..] { - // no params, nothing to elide - [] => unreachable!("handled by `elidable_lts.is_empty()`"), - [param] => { - if elidable_lts.contains(¶m.def_id) { - unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // 1. Start from the last elidable lifetime + // 2. While the lifetimes preceding it are also elidable, construct spans going from the current + // lifetime to the comma before it + // 3. Once this chain of elidable lifetimes stops, switch to constructing spans going from the + // current lifetime to the comma _after_ it + let mut end: Option = None; + elidable_lts + .iter() + .rev() + .map(|&id| { + let (idx, param) = explicit_params.iter().find_position(|param| param.def_id == id)?; + + let span = if let Some(next) = explicit_params.get(idx + 1) + && end != Some(next.def_id) + { + // Extend the current span forward, up until the next param in the list. + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) } else { - unreachable!("handled by `elidable_lts.is_empty()`") - } - }, - [_, _, ..] => { - // Given a list like `<'a, 'b, 'c, 'd, ..>`, - // - // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should - // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: - // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> - // ^^ ^^ ^^^^^^^^ - // - // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go - // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the - // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave - // the list valid: - // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> - // ^^ ^^ ^^ ^^^^ ^^^^^^^^ - // - // In case there is no such starting cluster, we only need to do the second part of the algorithm: - // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> - // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ - - // Split off the starting cluster - // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): - // ``` - // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { - // // there were no lifetime param that couldn't be elided - // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") - // }; - // match split { /* .. */ } - // ``` - let Some(split_pos) = explicit_params - .iter() - .position(|param| !elidable_lts.contains(¶m.def_id)) - else { - // there were no lifetime param that couldn't be elided - unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // Extend the current span back to include the comma following the previous + // param. If the span of the next param in the list has already been + // extended, we continue the chain. This is why we're iterating in reverse. + end = Some(param.def_id); + + // `idx` will never be 0, else we'd be removing the entire list of generics + let prev = explicit_params.get(idx - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) }; - let split = explicit_params - .split_at_checked(split_pos) - .expect("got `split_pos` from `position` on the same Vec"); - - match split { - ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), - ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), - (cluster, rest @ [rest_first, ..]) => { - // the span for the cluster - (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) - // the span for the remaining lifetimes (calculations independent of the cluster) - .chain( - rest.array_windows() - .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) - .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), - ) - .map(|sp| (sp, String::new())) - .collect() - }, - } - }, - } + + Some((span, String::new())) + }) + .collect::>>()? }; suggestions.extend(usages.iter().map(|&usage| { diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs deleted file mode 100644 index 65e922ac07d36..0000000000000 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ /dev/null @@ -1,141 +0,0 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::sym; -use rustc_errors::Applicability; -use rustc_hir::{Body, Closure, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; -use rustc_span::Symbol; - -pub struct LinesFilterMapOk { - msrv: Msrv, -} - -impl LinesFilterMapOk { - pub fn new(conf: &Conf) -> Self { - Self { msrv: conf.msrv } - } -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` - /// when `lines` has type `std::io::Lines`. - /// - /// ### Why is this bad? - /// `Lines` instances might produce a never-ending stream of `Err`, in which case - /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an - /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop, - /// even in the absence of explicit loops in the user code. - /// - /// This situation can arise when working with user-provided paths. On some platforms, - /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory, - /// but any later attempt to read from `fs` will return an error. - /// - /// ### Known problems - /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` - /// instance in all cases. There are two cases where the suggestion might not be - /// appropriate or necessary: - /// - /// - If the `Lines` instance can never produce any error, or if an error is produced - /// only once just before terminating the iterator, using `map_while()` is not - /// necessary but will not do any harm. - /// - If the `Lines` instance can produce intermittent errors then recover and produce - /// successful results, using `map_while()` would stop at the first error. - /// - /// ### Example - /// ```no_run - /// # use std::{fs::File, io::{self, BufRead, BufReader}}; - /// # let _ = || -> io::Result<()> { - /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok); - /// // If "some-path" points to a directory, the next statement never terminates: - /// let first_line: Option = lines.next(); - /// # Ok(()) }; - /// ``` - /// Use instead: - /// ```no_run - /// # use std::{fs::File, io::{self, BufRead, BufReader}}; - /// # let _ = || -> io::Result<()> { - /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok); - /// let first_line: Option = lines.next(); - /// # Ok(()) }; - /// ``` - #[clippy::version = "1.70.0"] - pub LINES_FILTER_MAP_OK, - suspicious, - "filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop" -} - -impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); - -impl LateLintPass<'_> for LinesFilterMapOk { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) - && let fm_method_name = fm_method.ident.name - && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && cx - .typeck_results() - .expr_ty_adjusted(fm_receiver) - .is_diag_item(cx, sym::IoLines) - && should_lint(cx, fm_args, fm_method_name) - && self.msrv.meets(cx, msrvs::MAP_WHILE) - { - span_lint_and_then( - cx, - LINES_FILTER_MAP_OK, - fm_span, - format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`",), - |diag| { - diag.span_note( - fm_receiver.span, - "this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error"); - diag.span_suggestion( - fm_span, - "replace with", - "map_while(Result::ok)", - Applicability::MaybeIncorrect, - ); - }, - ); - } - } -} - -fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> bool { - match args { - [] => method_name == sym::flatten, - [fm_arg] => { - match &fm_arg.kind { - // Detect `Result::ok` - ExprKind::Path(qpath) => cx - .qpath_res(qpath, fm_arg.hir_id) - .opt_def_id() - .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::result_ok_method, did)), - // Detect `|x| x.ok()` - ExprKind::Closure(Closure { body, .. }) => { - if let Body { - params: [param], value, .. - } = cx.tcx.hir_body(*body) - && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - { - method.ident.name == sym::ok - && receiver.res_local_id() == Some(param.pat.hir_id) - && cx - .typeck_results() - .type_dependent_def_id(value.hir_id) - .opt_parent(cx) - .opt_impl_ty(cx) - .is_diag_item(cx, sym::Result) - } else { - false - } - }, - _ => false, - } - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index c974d9cca7d23..a0de464ff7f81 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -157,10 +157,7 @@ pub(super) fn check<'tcx>( "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, )", ident.name)), - ( - span, - format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), - ), + (span, format!("{indexed}.{method}().enumerate(){method_1}{method_2}")), ], Applicability::HasPlaceholders, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 528cc64fa7bc5..0d37be17689a5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{ - Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, + Block, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, }; use rustc_lint::LateContext; use rustc_span::{BytePos, Span, sym}; @@ -75,12 +75,19 @@ pub(super) fn check<'tcx>( fn contains_any_break_or_continue(block: &Block<'_>) -> bool { for_each_expr_without_closures(block, |e| match e.kind { ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::InlineAsm(asm) if contains_label(asm) => ControlFlow::Break(()), ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), }) .is_some() } +fn contains_label(asm: &InlineAsm<'_>) -> bool { + asm.operands + .iter() + .any(|(op, _span)| matches!(op, InlineAsmOperand::Label { .. })) +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? @@ -378,7 +385,15 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, - InlineAsmOperand::Label { block } => never_loop_block(cx, block, local_labels, main_loop_id), + InlineAsmOperand::Label { block } => + // We do not know whether the label will be executed or not, so `Diverging` must be + // downgraded to `Normal`. + { + match never_loop_block(cx, block, local_labels, main_loop_id) { + NeverLoopResult::Diverging { .. } => NeverLoopResult::Normal, + result => result, + } + }, })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 298bf10754897..0f3d8b3366751 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -183,7 +183,13 @@ fn emit_manual_let_else( format!("{{ {sn_else} }}") }; let sn_bl = replace_in_pattern(cx, span, ident_map, pat, &mut app, true); - let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); + let sugg = if sn_expr.ends_with('}') { + // let-else statement expressions are not allowed to end with `}` + // https://rust-lang.github.io/rfcs/3137-let-else.html#let-pattern--if--else--else- + format!("let {sn_bl} = ({sn_expr}) else {else_bl};") + } else { + format!("let {sn_bl} = {sn_expr} else {else_bl};") + }; diag.span_suggestion(span, "consider writing", sugg, app); }, ); diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index 86f0eff9cd869..dce0d105f4c5b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -1,13 +1,13 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use clippy_utils::source::snippet_with_context; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -124,8 +124,7 @@ fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span, msrv: Msrv) { fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind && seg.ident.name == sym::as_ref - && let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind() - && cx.tcx.is_diagnostic_item(sym::Option, adtdef.did()) + && cx.typeck_results().expr_ty(callee).is_diag_item(cx, sym::Option) && msrv.meets( cx, if clippy_utils::is_in_const_context(cx) { @@ -135,19 +134,22 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { }, ) { - if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { - span_lint_and_sugg( - cx, - MANUAL_OPTION_AS_SLICE, - span, - "use `Option::as_slice`", - "use", - format!("{snippet}.as_slice()"), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`"); - } + span_lint_and_then( + cx, + MANUAL_OPTION_AS_SLICE, + span, + "manual implementation of `Option::as_slice`", + |diag| { + let mut app = Applicability::MachineApplicable; + let callee = snippet_with_context(cx, callee.span, expr.span.ctxt(), "_", &mut app).0; + diag.span_suggestion_verbose( + span, + "use `Option::as_slice` directly", + format!("{callee}.as_slice()"), + app, + ); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 681dc2ab5bc0c..b07d4fe81f8a9 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -214,6 +214,9 @@ fn lint_map_unit_fn( }; let fn_arg = &map_args.1[0]; + #[expect(clippy::items_after_statements, reason = "the const is only used below")] + const SUGG_MSG: &str = "use `if let` instead"; + if is_unit_function(cx, fn_arg) { let mut applicability = Applicability::MachineApplicable; let msg = suggestion_msg("function", map_type); @@ -226,7 +229,7 @@ fn lint_map_unit_fn( ); span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(stmt.span, "try", suggestion, applicability); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); @@ -242,7 +245,7 @@ fn lint_map_unit_fn( snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); - diag.span_suggestion(stmt.span, "try", suggestion, applicability); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", @@ -250,7 +253,7 @@ fn lint_map_unit_fn( snippet(cx, binding.pat.span, "_"), snippet(cx, var_arg.span, "_"), ); - diag.span_suggestion(stmt.span, "try", suggestion, Applicability::HasPlaceholders); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, Applicability::HasPlaceholders); } }); } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index e00d0c7f3d6c1..abbc43d8e9b04 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -11,7 +11,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_copy}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -87,7 +88,9 @@ fn handle( binding_id: HirId, ) { // Only deal with situations where both alternatives return the same non-adjusted type. - if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) { + if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) + || !safe_to_move_scrutinee(cx, expr, condition) + { return; } @@ -185,6 +188,29 @@ fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static } } +/// Checks whether it is safe to move scrutinee. +/// It is not safe to move if: +/// 1. `scrutinee` is a `Result` that doesn't implemenet `Copy`, mainly because the `Err` +/// variant is not copyable. +/// 2. `expr` is a local variable that is used after the if-let-else expression. +/// ```rust,ignore +/// let foo: Result = Ok(0); +/// let v = if let Ok(v) = foo { v } else { 1 }; +/// let bar = foo; +/// ``` +fn safe_to_move_scrutinee(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>) -> bool { + if let Some(hir_id) = scrutinee.res_local_id() + && let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee) + && scrutinee_ty.is_diag_item(cx, sym::Result) + && !is_copy(cx, scrutinee_ty) + && local_used_after_expr(cx, hir_id, expr) + { + false + } else { + true + } +} + pub fn check_match<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 156118be347f9..2ca656edc66eb 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::option_arg_ty; use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; @@ -10,60 +11,83 @@ use rustc_middle::ty; use super::MATCH_AS_REF; pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - let arm_ref_mut = if is_none_arm(cx, &arms[0]) { - is_ref_some_arm(cx, &arms[1]) - } else if is_none_arm(cx, &arms[1]) { - is_ref_some_arm(cx, &arms[0]) + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + && let Some(arm_ref_mutbl) = if is_none_arm(cx, arm1) { + as_ref_some_arm(cx, arm2) + } else if is_none_arm(cx, arm2) { + as_ref_some_arm(cx, arm1) } else { None + } + && let output_ty = cx.typeck_results().expr_ty(expr) + && let input_ty = cx.typeck_results().expr_ty(ex) + && let Some(input_ty) = option_arg_ty(cx, input_ty) + && let Some(output_ty) = option_arg_ty(cx, output_ty) + && let ty::Ref(_, output_ty, output_mutbl) = *output_ty.kind() + { + let method = match arm_ref_mutbl { + Mutability::Not => "as_ref", + Mutability::Mut => "as_mut", }; - if let Some(rb) = arm_ref_mut { - let suggestion = match rb { - Mutability::Not => "as_ref", - Mutability::Mut => "as_mut", - }; - let output_ty = cx.typeck_results().expr_ty(expr); - let input_ty = cx.typeck_results().expr_ty(ex); + // ``` + // let _: Option<&T> = match opt { + // Some(ref mut t) => Some(t), + // None => None, + // }; + // ``` + // We need to suggest `t.as_ref()` in order downcast the reference from `&mut` to `&`. + // We may or may not need to cast the type as well, for which we'd need `.map()`, and that could + // theoretically take care of the reference downcasting as well, but we chose to keep these two + // operations separate + let need_as_ref = arm_ref_mutbl == Mutability::Mut && output_mutbl == Mutability::Not; - let cast = if let ty::Adt(_, args) = input_ty.kind() - && let input_ty = args.type_at(0) - && let ty::Adt(_, args) = output_ty.kind() - && let output_ty = args.type_at(0) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() - && input_ty != output_ty - { - ".map(|x| x as _)" - } else { - "" - }; + let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MATCH_AS_REF, - expr.span, - format!("use `{suggestion}()` instead"), - "try", - format!( - "{}.{suggestion}(){cast}", - snippet_with_applicability(cx, ex.span, "_", &mut applicability), - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then( + cx, + MATCH_AS_REF, + expr.span, + format!("manual implementation of `Option::{method}`"), + |diag| { + if need_as_ref { + diag.note("but the type is coerced to a non-mutable reference, and so `as_ref` can used instead"); + diag.span_suggestion_verbose( + expr.span, + "use `Option::as_ref()`", + format!( + "{}.as_ref(){cast}", + Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + ), + applicability, + ); + } else { + diag.span_suggestion_verbose( + expr.span, + format!("use `Option::{method}()` directly"), + format!( + "{}.{method}(){cast}", + Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + ), + applicability, + ); + } + }, + ); } } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) -fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { +fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind && cx .qpath_res(qpath, arm.pat.hir_id) .ctor_parent(cx) .is_lang_item(cx, LangItem::OptionSome) - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index ac5da320bdffb..fa44a56af182d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -108,7 +108,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { }, _, ) => path_prefix.with_prefix(path.segments), - _ => (), + QPath::TypeRelative(..) => (), } }); } diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index c9b6821ad98fd..9c6cf66019f01 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -172,7 +172,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, )), ) => { - return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; + return !matches!(annot, BindingMode(ByRef::Yes(..), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` ( diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index d39e315cae1f2..7a1dd94567b14 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -176,7 +176,7 @@ fn get_pat_binding<'tcx>( if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local { - if matches!(bind_annot.0, rustc_ast::ByRef::Yes(_)) { + if matches!(bind_annot.0, rustc_ast::ByRef::Yes(..)) { let _ = byref_ident.insert(ident); } // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 2ca76a1fe6945..a0f88cf911d84 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -416,7 +416,7 @@ fn get_ident(path: &QPath<'_>) -> Option { let name = path.segments[0].ident; Some(name) }, - _ => None, + QPath::TypeRelative(..) => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 8980a22ad6131..18087bd96debf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -57,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) _ => false, }, // local binding capturing a reference - Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..)) => { + Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingMode(ByRef::Yes(..), _), ..)) => { return; }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/methods/lines_filter_map_ok.rs new file mode 100644 index 0000000000000..baf5b6f93f637 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/lines_filter_map_ok.rs @@ -0,0 +1,85 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::{Body, Closure, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::LINES_FILTER_MAP_OK; + +pub(super) fn check_flatten(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, msrv: Msrv) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .is_diag_item(cx, sym::IoLines) + && msrv.meets(cx, msrvs::MAP_WHILE) + { + emit(cx, recv, "flatten", call_span); + } +} + +pub(super) fn check_filter_or_flat_map( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + method_name: &'static str, + method_arg: &Expr<'_>, + call_span: Span, + msrv: Msrv, +) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .is_diag_item(cx, sym::IoLines) + && match method_arg.kind { + // Detect `Result::ok` + ExprKind::Path(ref qpath) => cx + .qpath_res(qpath, method_arg.hir_id) + .is_diag_item(cx, sym::result_ok_method), + // Detect `|x| x.ok()` + ExprKind::Closure(&Closure { body, .. }) => { + if let Body { + params: [param], value, .. + } = cx.tcx.hir_body(body) + && let ExprKind::MethodCall(method, receiver, [], _) = value.kind + { + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx.ty_based_def(*value).is_diag_item(cx, sym::result_ok_method) + } else { + false + } + }, + _ => false, + } + && msrv.meets(cx, msrvs::MAP_WHILE) + { + emit(cx, recv, method_name, call_span); + } +} + +fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, method_name: &'static str, call_span: Span) { + span_lint_and_then( + cx, + LINES_FILTER_MAP_OK, + call_span, + format!("`{method_name}()` will run forever if the iterator repeatedly produces an `Err`"), + |diag| { + diag.span_note( + recv.span, + "this expression returning a `std::io::Lines` may produce \ + an infinite number of `Err` in case of a read error", + ); + diag.span_suggestion( + call_span, + "replace with", + "map_while(Result::ok)", + Applicability::MaybeIncorrect, + ); + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 48fa5f7bdc6ef..20dfce914838f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -56,6 +56,7 @@ mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; mod lib; +mod lines_filter_map_ok; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -4665,6 +4666,55 @@ declare_clippy_lint! { "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` + /// when `lines` has type `std::io::Lines`. + /// + /// ### Why is this bad? + /// `Lines` instances might produce a never-ending stream of `Err`, in which case + /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an + /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop, + /// even in the absence of explicit loops in the user code. + /// + /// This situation can arise when working with user-provided paths. On some platforms, + /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory, + /// but any later attempt to read from `fs` will return an error. + /// + /// ### Known problems + /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` + /// instance in all cases. There are two cases where the suggestion might not be + /// appropriate or necessary: + /// + /// - If the `Lines` instance can never produce any error, or if an error is produced + /// only once just before terminating the iterator, using `map_while()` is not + /// necessary but will not do any harm. + /// - If the `Lines` instance can produce intermittent errors then recover and produce + /// successful results, using `map_while()` would stop at the first error. + /// + /// ### Example + /// ```no_run + /// # use std::{fs::File, io::{self, BufRead, BufReader}}; + /// # let _ = || -> io::Result<()> { + /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok); + /// // If "some-path" points to a directory, the next statement never terminates: + /// let first_line: Option = lines.next(); + /// # Ok(()) }; + /// ``` + /// Use instead: + /// ```no_run + /// # use std::{fs::File, io::{self, BufRead, BufReader}}; + /// # let _ = || -> io::Result<()> { + /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok); + /// let first_line: Option = lines.next(); + /// # Ok(()) }; + /// ``` + #[clippy::version = "1.70.0"] + pub LINES_FILTER_MAP_OK, + suspicious, + "filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4847,6 +4897,7 @@ impl_lint_pass!(Methods => [ IP_CONSTANT, REDUNDANT_ITER_CLONED, UNNECESSARY_OPTION_MAP_OR_ELSE, + LINES_FILTER_MAP_OK, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -5050,7 +5101,11 @@ impl Methods { (sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv), (sym::cloned, []) => { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); - option_as_ref_cloned::check(cx, recv, span); + if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = + method_call(recv) + { + option_as_ref_cloned::check(cx, span, method, as_ref_recv, as_ref_ident_span); + } }, (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); @@ -5162,32 +5217,47 @@ impl Methods { }, (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, name); + unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FilterMap); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); + lines_filter_map_ok::check_filter_or_flat_map( + cx, + expr, + recv, + "filter_map", + arg, + call_span, + self.msrv, + ); }, (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, name); + unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap); }, (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); + lines_filter_map_ok::check_filter_or_flat_map( + cx, expr, recv, "flat_map", arg, call_span, self.msrv, + ); }, - (sym::flatten, []) => match method_call(recv) { - Some((sym::map, recv, [map_arg], map_span, _)) => { - map_flatten::check(cx, expr, recv, map_arg, map_span); - }, - Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::LaterCloned, - true, - ), - _ => {}, + (sym::flatten, []) => { + match method_call(recv) { + Some((sym::map, recv, [map_arg], map_span, _)) => { + map_flatten::check(cx, expr, recv, map_arg, map_span); + }, + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::LaterCloned, + true, + ), + _ => {}, + } + lines_filter_map_ok::check_flatten(cx, expr, recv, call_span, self.msrv); }, (sym::fold, [init, acc]) => { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index 591f6aacaef87..156c624488eba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -4,24 +4,28 @@ use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; -use super::{OPTION_AS_REF_CLONED, method_call}; +use super::OPTION_AS_REF_CLONED; -pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { - if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = - method_call(cloned_recv) - && cx - .typeck_results() - .expr_ty(as_ref_recv) - .peel_refs() - .is_diag_item(cx, sym::Option) +pub(super) fn check( + cx: &LateContext<'_>, + cloned_ident_span: Span, + as_ref_method: Symbol, + as_ref_recv: &Expr<'_>, + as_ref_ident_span: Span, +) { + if cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, OPTION_AS_REF_CLONED, as_ref_ident_span.to(cloned_ident_span), - format!("cloning an `Option<_>` using `.{method}().cloned()`"), + format!("cloning an `Option<_>` using `.{as_ref_method}().cloned()`"), "this can be written more concisely by cloning the `Option<_>` directly", "clone".into(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index c9c75f3f38e2d..8732eba6d4e87 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; @@ -34,79 +34,67 @@ pub(super) fn check<'tcx>( { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); - if search_snippet.lines().count() <= 1 { - // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` - // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` - let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if search_method == sym::find - && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind - && let closure_body = cx.tcx.hir_body(body) - && let Some(closure_arg) = closure_body.params.first() - { - if let PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { - // `find()` provides a reference to the item, but `any` does not, - // so we should fix item usages for suggestion - if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { - applicability = closure_sugg.applicability; - Some(closure_sugg.suggestion) - } else { - Some(search_snippet.to_string()) - } + // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` + // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` + let mut applicability = Applicability::MachineApplicable; + let any_search_snippet = if search_method == sym::find + && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind + && let closure_body = cx.tcx.hir_body(body) + && let Some(closure_arg) = closure_body.params.first() + { + if let PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { + // `find()` provides a reference to the item, but `any` does not, + // so we should fix item usages for suggestion + if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { + applicability = closure_sugg.applicability; + Some(closure_sugg.suggestion) } else { - None + Some(search_snippet.to_string()) } } else { None - }; - // add note if not multi-line - if is_some { - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "consider using", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - applicability, - ); - } else { - let iter = snippet(cx, search_recv.span, ".."); - let sugg = if is_receiver_of_method_call(cx, expr) { - format!( - "(!{iter}.any({}))", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ) - } else { - format!( - "!{iter}.any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ) - }; - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - expr.span, - msg, - "consider using", - sugg, - applicability, - ); } } else { - let hint = format!( - "this is more succinctly expressed by calling `any()`{}", - if option_check_method == "is_none" { - " with negation" - } else { - "" - } + None + }; + // add note if not multi-line + if is_some { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + msg, + "consider using", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + applicability, + ); + } else { + let iter = snippet(cx, search_recv.span, ".."); + let sugg = if is_receiver_of_method_call(cx, expr) { + format!( + "(!{iter}.any({}))", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + } else { + format!( + "!{iter}.any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + }; + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + msg, + "consider using", + sugg, + applicability, ); - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, msg, None, hint); } } // lint if `find()` is called by `String` or `&str` diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index d9d642015063e..7f729ac7ca94e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,15 +2,15 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sym; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{is_copy, option_arg_ty}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::Symbol; +use rustc_span::Span; +use std::fmt::Display; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; @@ -18,7 +18,8 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, - name: Symbol, + call_span: Span, + kind: Kind, ) { if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; @@ -45,61 +46,88 @@ pub(super) fn check<'tcx>( let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) - if name == sym::filter_map - && let hir::ExprKind::Call(expr, args) = body.value.kind + if kind.is_filter_map() + && let hir::ExprKind::Call(expr, [arg]) = body.value.kind && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && let hir::ExprKind::Path(_) = args[0].kind + && let hir::ExprKind::Path(_) = arg.kind { span_lint( cx, UNNECESSARY_FILTER_MAP, - expr.span, + call_span, String::from("this call to `.filter_map(..)` is unnecessary"), ); return; } - if name == sym::filter_map { - "map(..)" - } else { - "map(..).next()" + match kind { + Kind::FilterMap => "map(..)", + Kind::FindMap => "map(..).next()", } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { - match cx.typeck_results().expr_ty(body.value).kind() { - ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => - { - if name == sym::filter_map { - "filter(..)" - } else { - "find(..)" - } - }, - _ => return, + let ty = cx.typeck_results().expr_ty(body.value); + if option_arg_ty(cx, ty).is_some_and(|t| t == in_ty) { + match kind { + Kind::FilterMap => "filter(..)", + Kind::FindMap => "find(..)", + } + } else { + return; } } else { return; }; span_lint( cx, - if name == sym::filter_map { - UNNECESSARY_FILTER_MAP - } else { - UNNECESSARY_FIND_MAP + match kind { + Kind::FilterMap => UNNECESSARY_FILTER_MAP, + Kind::FindMap => UNNECESSARY_FIND_MAP, }, - expr.span, - format!("this `.{name}(..)` can be written more simply using `.{sugg}`"), + call_span, + format!("this `.{kind}(..)` can be written more simply using `.{sugg}`"), ); } } +#[derive(Clone, Copy)] +pub(super) enum Kind { + FilterMap, + FindMap, +} + +impl Kind { + fn is_filter_map(self) -> bool { + matches!(self, Self::FilterMap) + } +} + +impl Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::FilterMap => f.write_str("filter_map"), + Self::FindMap => f.write_str("find_map"), + } + } +} + // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + // None + (false, true) + }, hir::ExprKind::Call(func, args) => { if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { if args[0].res_local_id() == Some(arg_id) { + // Some(arg_id) return (false, false); } + // Some(not arg_id) return (true, false); } (true, true) @@ -109,8 +137,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc && cx.typeck_results().expr_ty(recv).is_bool() && arg.res_local_id() == Some(arg_id) { + // bool.then_some(arg_id) (false, true) } else { + // bool.then_some(not arg_id) (true, true) } }, @@ -134,14 +164,6 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) - if cx - .qpath_res(path, expr.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone) => - { - (false, true) - }, _ => (true, true), } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index fffaf40c9d141..0eb5c36a28a2a 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -8,7 +8,7 @@ use super::UNNEEDED_WILDCARD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind - && let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) + && let Some(rest_index) = patterns.iter().position(Pat::is_rest) { if let Some((left_index, left_pat)) = patterns[..rest_index] .iter() diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index c7c4976aeb7b2..25fcc7ee568e9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { fn path_has_args(p: &QPath<'_>) -> bool { match *p { QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), - _ => false, + QPath::Resolved(..) => false, } } diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_ifs.rs similarity index 88% rename from src/tools/clippy/clippy_lints/src/needless_if.rs rename to src/tools/clippy/clippy_lints/src/needless_ifs.rs index c90019f6ee161..8ec7e47ccc5bc 100644 --- a/src/tools/clippy/clippy_lints/src/needless_if.rs +++ b/src/tools/clippy/clippy_lints/src/needless_ifs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -29,13 +29,13 @@ declare_clippy_lint! { /// really_expensive_condition_with_side_effects(&mut i); /// ``` #[clippy::version = "1.72.0"] - pub NEEDLESS_IF, + pub NEEDLESS_IFS, complexity, "checks for empty if branches" } -declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]); +declare_lint_pass!(NeedlessIfs => [NEEDLESS_IFS]); -impl LateLintPass<'_> for NeedlessIf { +impl LateLintPass<'_> for NeedlessIfs { fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) { if let StmtKind::Expr(expr) = stmt.kind && let Some(If { @@ -56,12 +56,13 @@ impl LateLintPass<'_> for NeedlessIf { src.bytes() .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace()) }) - && let Some(cond_snippet) = cond.span.get_source_text(cx) + && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) + && let Some(cond_snippet) = cond_span.get_source_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( cx, - NEEDLESS_IF, + NEEDLESS_IFS, stmt.span, "this `if` branch is empty", "you can remove it", diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 51644f7dce6cf..f756f94d97b39 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -6,7 +6,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; -use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemImplKind, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; +use rustc_hir::{ + Body, Expr, ExprKind, HirId, ImplItem, ImplItemImplKind, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, ConstKind, GenericArgKind, GenericArgsRef}; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs index 54f50f11e0349..71982023779e0 100644 --- a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs @@ -8,46 +8,52 @@ use rustc_span::Span; use super::DOUBLE_COMPARISONS; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { - let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { - (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { - (lb.node, llhs, lrhs, rb.node, rlhs, rrhs) - }, - _ => return, - }; - if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { - return; - } - macro_rules! lint_double_comparison { - ($op:tt) => {{ - let mut applicability = Applicability::MachineApplicable; - let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability); - let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability); - let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str); - span_lint_and_sugg( - cx, - DOUBLE_COMPARISONS, - span, - "this binary expression can be simplified", - "try", - sugg, - applicability, - ); - }}; - } - match (op, lkind, rkind) { - (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { - lint_double_comparison!(<=); - }, - (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { - lint_double_comparison!(>=); - }, - (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { - lint_double_comparison!(!=); - }, - (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { - lint_double_comparison!(==); - }, - _ => (), +pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) { + if let ExprKind::Binary(lop, llhs, lrhs) = lhs.kind + && let ExprKind::Binary(rop, rlhs, rrhs) = rhs.kind + && eq_expr_value(cx, llhs, rlhs) + && eq_expr_value(cx, lrhs, rrhs) + { + let op = match (op, lop.node, rop.node) { + // x == y || x < y => x <= y + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) + // x < y || x == y => x <= y + | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { + "<=" + }, + // x == y || x > y => x >= y + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) + // x > y || x == y => x >= y + | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { + ">=" + }, + // x < y || x > y => x != y + (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) + // x > y || x < y => x != y + | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { + "!=" + }, + // x <= y && x >= y => x == y + (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) + // x >= y && x <= y => x == y + | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { + "==" + }, + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability); + let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability); + let sugg = format!("{lhs_str} {op} {rhs_str}"); + span_lint_and_sugg( + cx, + DOUBLE_COMPARISONS, + span, + "this binary expression can be simplified", + "try", + sugg, + applicability, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division_remainder_used.rs new file mode 100644 index 0000000000000..976b2d8b0c63a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division_remainder_used.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::BinOpKind; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use super::INTEGER_DIVISION_REMAINDER_USED; + +pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) { + if let BinOpKind::Div | BinOpKind::Rem = op + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let rhs_ty = cx.typeck_results().expr_ty(rhs) + && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() + && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() + { + span_lint( + cx, + INTEGER_DIVISION_REMAINDER_USED, + span.source_callsite(), + format!("use of `{}` has been disallowed in this context", op.as_str()), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/invalid_upcast_comparisons.rs similarity index 56% rename from src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs rename to src/tools/clippy/clippy_lints/src/operators/invalid_upcast_comparisons.rs index 885649074ab63..b32848a323375 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/invalid_upcast_comparisons.rs @@ -1,9 +1,8 @@ use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, IntTy, UintTy}; -use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::comparisons; @@ -12,29 +11,26 @@ use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_with_context; -declare_clippy_lint! { - /// ### What it does - /// Checks for comparisons where the relation is always either - /// true or false, but where one side has been upcast so that the comparison is - /// necessary. Only integer types are checked. - /// - /// ### Why is this bad? - /// An expression like `let x : u8 = ...; (x as u32) > 300` - /// will mistakenly imply that it is possible for `x` to be outside the range of - /// `u8`. - /// - /// ### Example - /// ```no_run - /// let x: u8 = 1; - /// (x as u32) > 300; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INVALID_UPCAST_COMPARISONS, - pedantic, - "a comparison involving an upcast which is always true or false" -} +use super::INVALID_UPCAST_COMPARISONS; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + cmp: BinOpKind, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, + span: Span, +) { + let normalized = comparisons::normalize_comparison(cmp, lhs, rhs); + let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { + return; + }; -declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); + let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); + let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); + + upcast_comparison_bounds_err(cx, span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); + upcast_comparison_bounds_err(cx, span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); +} fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> { if let ExprKind::Cast(cast_exp, _) = expr.kind { @@ -68,29 +64,6 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< } } -fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(cast_val, _) = expr.kind { - let mut applicability = Applicability::MachineApplicable; - let (cast_val_snip, _) = snippet_with_context( - cx, - cast_val.span, - expr.span.ctxt(), - "the expression", - &mut applicability, - ); - span_lint( - cx, - INVALID_UPCAST_COMPARISONS, - span, - format!( - "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - cast_val_snip, - if always { "true" } else { "false" }, - ), - ); - } -} - fn upcast_comparison_bounds_err<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -103,63 +76,54 @@ fn upcast_comparison_bounds_err<'tcx>( if let Some((lb, ub)) = lhs_bounds && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val + match rel { + Rel::Eq => { + if norm_rhs_val < lb || ub < norm_rhs_val { + err_upcast_comparison(cx, span, lhs, false); } }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val + Rel::Ne => { + if norm_rhs_val < lb || ub < norm_rhs_val { + err_upcast_comparison(cx, span, lhs, true); } }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true); - } else if match rel { Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val + if (invert && norm_rhs_val < lb) || (!invert && ub < norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, true); + } else if (!invert && norm_rhs_val <= lb) || (invert && ub <= norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, false); } }, Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val + if (invert && norm_rhs_val <= lb) || (!invert && ub <= norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, true); + } else if (!invert && norm_rhs_val < lb) || (invert && ub < norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, false); } }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false); } } } -impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { - let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { - return; - }; - - let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); - let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); - - upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); - upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); - } +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { + if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); + span_lint( + cx, + INVALID_UPCAST_COMPARISONS, + span, + format!( + "because of the numeric bounds on `{}` prior to casting, this expression is always {}", + cast_val_snip, + if always { "true" } else { "false" }, + ), + ); } } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs similarity index 50% rename from src/tools/clippy/clippy_lints/src/manual_div_ceil.rs rename to src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs index ee531741a5153..98aa474215370 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs @@ -1,4 +1,3 @@ -use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; @@ -7,111 +6,69 @@ use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::LateContext; use rustc_middle::ty::{self}; -use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -declare_clippy_lint! { - /// ### What it does - /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation - /// of `x.div_ceil(y)`. - /// - /// ### Why is this bad? - /// It's simpler, clearer and more readable. - /// - /// ### Example - /// ```no_run - /// let x: i32 = 7; - /// let y: i32 = 4; - /// let div = (x + (y - 1)) / y; - /// ``` - /// Use instead: - /// ```no_run - /// #![feature(int_roundings)] - /// let x: i32 = 7; - /// let y: i32 = 4; - /// let div = x.div_ceil(y); - /// ``` - #[clippy::version = "1.83.0"] - pub MANUAL_DIV_CEIL, - complexity, - "manually reimplementing `div_ceil`" -} - -pub struct ManualDivCeil { - msrv: Msrv, -} - -impl ManualDivCeil { - #[must_use] - pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } - } -} +use super::MANUAL_DIV_CEIL; -impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]); +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, msrv: Msrv) { + let mut applicability = Applicability::MachineApplicable; -impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; - - if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind - && div_op.node == BinOpKind::Div - && check_int_ty_and_feature(cx, div_lhs) - && check_int_ty_and_feature(cx, div_rhs) - && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind - && self.msrv.meets(cx, msrvs::DIV_CEIL) + if op == BinOpKind::Div + && check_int_ty_and_feature(cx, lhs) + && check_int_ty_and_feature(cx, rhs) + && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = lhs.kind + && msrv.meets(cx, msrvs::DIV_CEIL) + { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) { - // (x + (y - 1)) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) - { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - return; - } + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + return; + } - // ((y - 1) + x) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) - { - build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); - return; - } + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) + { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + return; + } - // (x + y - 1) / y - if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Sub - && add_op.node == BinOpKind::Add - && check_literal(inner_rhs) - && check_eq_expr(cx, add_rhs, div_rhs) - { - build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); - } + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, rhs) + { + build_suggestion(cx, expr, add_lhs, rhs, &mut applicability); + } - // (x + (Y - 1)) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - } + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + } - // ((Y - 1) + x) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) { - build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); - } + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + } - // (x - (-Y - 1)) / Y - if inner_op.node == BinOpKind::Sub - && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind - && differ_by_one(abs_div_rhs, inner_rhs) - { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - } + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index aaea4ff11fc37..8db2cc1d3f578 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -11,6 +11,9 @@ mod float_cmp; mod float_equality_without_abs; mod identity_op; mod integer_division; +mod integer_division_remainder_used; +mod invalid_upcast_comparisons; +mod manual_div_ceil; mod manual_is_multiple_of; mod manual_midpoint; mod misrefactored_assign_op; @@ -463,7 +466,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for statements of the form `(a - b) < f32::EPSILON` or - /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// `(a - b) < f64::EPSILON`. Note the missing `.abs()`. /// /// ### Why is this bad? /// The code without `.abs()` is more likely to have a bug. @@ -616,7 +619,7 @@ declare_clippy_lint! { /// println!("{within_tolerance}"); // true /// ``` /// - /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// NOTE: Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is /// a different use of the term that is not suitable for floating point equality comparison. /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. /// @@ -679,7 +682,7 @@ declare_clippy_lint! { /// println!("{within_tolerance}"); // true /// ``` /// - /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// NOTE: Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is /// a different use of the term that is not suitable for floating point equality comparison. /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. /// @@ -860,6 +863,78 @@ declare_clippy_lint! { "manual implementation of `.is_multiple_of()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation + /// of `x.div_ceil(y)`. + /// + /// ### Why is this bad? + /// It's simpler, clearer and more readable. + /// + /// ### Example + /// ```no_run + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = (x + (y - 1)) / y; + /// ``` + /// Use instead: + /// ```no_run + /// #![feature(int_roundings)] + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = x.div_ceil(y); + /// ``` + #[clippy::version = "1.83.0"] + pub MANUAL_DIV_CEIL, + complexity, + "manually reimplementing `div_ceil`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons where the relation is always either + /// true or false, but where one side has been upcast so that the comparison is + /// necessary. Only integer types are checked. + /// + /// ### Why is this bad? + /// An expression like `let x : u8 = ...; (x as u32) > 300` + /// will mistakenly imply that it is possible for `x` to be outside the range of + /// `u8`. + /// + /// ### Example + /// ```no_run + /// let x: u8 = 1; + /// (x as u32) > 300; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INVALID_UPCAST_COMPARISONS, + pedantic, + "a comparison involving an upcast which is always true or false" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of division (`/`) and remainder (`%`) operations + /// when performed on any integer types using the default `Div` and `Rem` trait implementations. + /// + /// ### Why restrict this? + /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, + /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). + /// + /// ### Example + /// ```no_run + /// let my_div = 10 / 2; + /// ``` + /// Use instead: + /// ```no_run + /// let my_div = 10 >> 1; + /// ``` + #[clippy::version = "1.79.0"] + pub INTEGER_DIVISION_REMAINDER_USED, + restriction, + "use of disallowed default division and remainder operations" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, @@ -897,6 +972,7 @@ impl_lint_pass!(Operators => [ FLOAT_EQUALITY_WITHOUT_ABS, IDENTITY_OP, INTEGER_DIVISION, + INTEGER_DIVISION_REMAINDER_USED, CMP_OWNED, FLOAT_CMP, FLOAT_CMP_CONST, @@ -906,6 +982,8 @@ impl_lint_pass!(Operators => [ SELF_ASSIGNMENT, MANUAL_MIDPOINT, MANUAL_IS_MULTIPLE_OF, + MANUAL_DIV_CEIL, + INVALID_UPCAST_COMPARISONS, ]); impl<'tcx> LateLintPass<'tcx> for Operators { @@ -921,6 +999,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { } erasing_op::check(cx, e, op.node, lhs, rhs); identity_op::check(cx, e, op.node, lhs, rhs); + invalid_upcast_comparisons::check(cx, op.node, lhs, rhs, e.span); 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); @@ -933,6 +1012,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { duration_subsec::check(cx, e, op.node, lhs, rhs); float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); + integer_division_remainder_used::check(cx, op.node, lhs, rhs, e.span); cmp_owned::check(cx, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); @@ -944,6 +1024,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { rhs, self.modulo_arithmetic_allow_comparison_to_zero, ); + manual_div_ceil::check(cx, e, op.node, lhs, rhs, self.msrv); }, ExprKind::AssignOp(op, lhs, rhs) => { let bin_op = op.node.into(); diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index ec6835db897ed..034fe8edc715d 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}; use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; @@ -10,7 +10,8 @@ use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does /// Checks for operations where precedence may be unclear and suggests to add parentheses. - /// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses + /// It catches a mixed usage of arithmetic and bit shifting/combining operators, + /// as well as method calls applied to closures. /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -109,6 +110,19 @@ impl EarlyLintPass for Precedence { }, _ => (), } + } else if let ExprKind::MethodCall(method_call) = &expr.kind + && let ExprKind::Closure(closure) = &method_call.receiver.kind + { + span_lint_and_then(cx, PRECEDENCE, expr.span, "precedence might not be obvious", |diag| { + diag.multipart_suggestion( + "consider parenthesizing the closure", + vec![ + (closure.fn_decl_span.shrink_to_lo(), String::from("(")), + (closure.body.span.shrink_to_hi(), String::from(")")), + ], + Applicability::MachineApplicable, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 1a6165d0af835..14675015c35e0 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -150,7 +150,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( - BindingMode(ByRef::Yes(ref_mutability), binding_mutability), + BindingMode(ByRef::Yes(_,ref_mutability), binding_mutability), _hir_id, ident, subpattern, @@ -169,7 +169,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { // Handle subpattern (@ subpattern) let maybe_subpattern = match subpattern { Some(Pat { - kind: PatKind::Binding(BindingMode(ByRef::Yes(_), _), _, subident, None), + kind: PatKind::Binding(BindingMode(ByRef::Yes(..), _), _, subident, None), .. }) => { // avoid `&ref` @@ -504,8 +504,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); let method_call_str = match by_ref { - ByRef::Yes(Mutability::Mut) => ".as_mut()", - ByRef::Yes(Mutability::Not) => ".as_ref()", + ByRef::Yes(_, Mutability::Mut) => ".as_mut()", + ByRef::Yes(_, Mutability::Not) => ".as_ref()", ByRef::No => "", }; let sugg = format!( diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 960edc6b02803..412ca2fa4ed9a 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -86,8 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind - else { + let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs index 074b79263d377..250c277ab5e1f 100644 --- a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs +++ b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { ) { if !matches!(k, FnKind::Closure) { for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + if let PatKind::Binding(BindingMode(ByRef::Yes(..), _), ..) = arg.pat.kind && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) && !arg.span.in_external_macro(cx.tcx.sess.source_map()) { @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/types/option_option.rs b/src/tools/clippy/clippy_lints/src/types/option_option.rs index 10df007f2a13d..6d57bb6ef3a0d 100644 --- a/src/tools/clippy/clippy_lints/src/types/option_option.rs +++ b/src/tools/clippy/clippy_lints/src/types/option_option.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qpath_generic_tys; use clippy_utils::res::MaybeResPath; +use clippy_utils::source::snippet; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -13,12 +14,21 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ && let Some(arg) = qpath_generic_tys(qpath).next() && arg.basic_res().opt_def_id() == Some(def_id) { - span_lint( + span_lint_and_then( cx, OPTION_OPTION, hir_ty.span, - "consider using `Option` instead of `Option>` or a custom \ - enum if you need to distinguish all 3 cases", + // use just `T` here, as the inner type is not what's problematic + "use of `Option>`", + |diag| { + // but use the specific type here, as: + // - this is kind of a suggestion + // - it's printed right after the linted type + let inner_opt = snippet(cx, arg.span, "_"); + diag.help(format!( + "consider using `{inner_opt}`, or a custom enum if you need to distinguish all 3 cases" + )); + }, ); true } else { diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index ad4f3d450db86..4621c22d6b532 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -454,6 +454,6 @@ fn extend_with_matching( fn eq_pre_post(ps1: &[Pat], ps2: &[Pat], idx: usize) -> bool { ps1.len() == ps2.len() && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. - && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) - && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) + && over(&ps1[..idx], &ps2[..idx], eq_pat) + && over(&ps1[idx + 1..], &ps2[idx + 1..], eq_pat) } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 222427cd3075f..129e8a45aa892 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -743,10 +743,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ann = match ann { BindingMode::NONE => "NONE", BindingMode::REF => "REF", + BindingMode::REF_PIN => "REF_PIN", BindingMode::MUT => "MUT", BindingMode::REF_MUT => "REF_MUT", + BindingMode::REF_PIN_MUT => "REF_PIN_MUT", BindingMode::MUT_REF => "MUT_REF", + BindingMode::MUT_REF_PIN => "MUT_REF_PIN", BindingMode::MUT_REF_MUT => "MUT_REF_MUT", + BindingMode::MUT_REF_PIN_MUT => "MUT_REF_PIN_MUT", }; kind!("Binding(BindingMode::{ann}, _, {name}, {sub})"); self.ident(name); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index c55c5ec2f51ae..c39e4a4cc9562 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -9,7 +9,7 @@ use rustc_ast::{ FormatPlaceholder, FormatTrait, }; use rustc_errors::Applicability; -use rustc_hir::{Expr, Impl, Item, ItemKind}; +use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span}; @@ -240,7 +240,8 @@ declare_clippy_lint! { pub struct Write { format_args: FormatArgsStorage, - in_debug_impl: bool, + // The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated + outermost_debug_impl: Option, allow_print_in_tests: bool, } @@ -248,10 +249,14 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - in_debug_impl: false, + outermost_debug_impl: None, allow_print_in_tests: conf.allow_print_in_tests, } } + + fn in_debug_impl(&self) -> bool { + self.outermost_debug_impl.is_some() + } } impl_lint_pass!(Write => [ @@ -268,14 +273,16 @@ impl_lint_pass!(Write => [ impl<'tcx> LateLintPass<'tcx> for Write { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = true; + // Only check for `impl Debug`s if we're not already in one + if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) { + self.outermost_debug_impl = Some(item.owner_id); } } - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = false; + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + // Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl + if self.outermost_debug_impl == Some(item.owner_id) { + self.outermost_debug_impl = None; } } @@ -329,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { check_literal(cx, format_args, name); - if !self.in_debug_impl { + if !self.in_debug_impl() { for piece in &format_args.template { if let &FormatArgsPiece::Placeholder(FormatPlaceholder { span: Some(span), diff --git a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index e529bc2d2fa5b..6d5c7b86a0aed 100644 --- a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -5,7 +5,7 @@ use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; +use rustc_middle::ty::{self, GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_tool_lint! { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index d58b47bf6deb3..a1e1b763dccb1 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.92" +version = "0.1.93" 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/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 9d12b46b9546f..45463b4fa1db8 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-10-16 +nightly-2025-10-31 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 839b46325b5ea..e797c96156045 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -48,12 +48,10 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Box(l), Box(r)) | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), - (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), + (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, eq_pat), (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { - eq_maybe_qself(lqself.as_deref(), rqself.as_deref()) - && eq_path(lp, rp) - && over(lfs, rfs, |l, r| eq_pat(l, r)) + eq_maybe_qself(lqself.as_deref(), rqself.as_deref()) && eq_path(lp, rp) && over(lfs, rfs, eq_pat) }, (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { lr == rr @@ -61,7 +59,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat) }, - (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), + (Or(ls), Or(rs)) => unordered_over(ls, rs, eq_pat), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), _ => false, } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 6f0e57bd3ab1c..2bdd5739a5579 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -211,7 +211,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => + if self + .cx + .typeck_results() + .expr_ty(e) + .builtin_deref(true) + .is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 3904818cc9b6f..7d6787fec295d 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -209,7 +209,7 @@ pub struct Range<'a> { pub end: Option<&'a Expr<'a>>, /// Whether the interval is open or closed. pub limits: ast::RangeLimits, - pub span: Span + pub span: Span, } impl<'a> Range<'a> { @@ -236,14 +236,12 @@ impl<'a> Range<'a> { limits: ast::RangeLimits::HalfOpen, span, }), - (hir::LangItem::RangeFrom, [field]) if field.ident.name == sym::start => { - Some(Range { - start: Some(field.expr), - end: None, - limits: ast::RangeLimits::HalfOpen, - span, - }) - }, + (hir::LangItem::RangeFrom, [field]) if field.ident.name == sym::start => Some(Range { + start: Some(field.expr), + end: None, + limits: ast::RangeLimits::HalfOpen, + span, + }), (hir::LangItem::Range, [field1, field2]) => { let (start, end) = match (field1.ident.name, field2.ident.name) { (sym::start, sym::end) => (field1.expr, field2.expr), @@ -257,14 +255,12 @@ impl<'a> Range<'a> { span, }) }, - (hir::LangItem::RangeToInclusive, [field]) if field.ident.name == sym::end => { - Some(Range { - start: None, - end: Some(field.expr), - limits: ast::RangeLimits::Closed, - span, - }) - }, + (hir::LangItem::RangeToInclusive, [field]) if field.ident.name == sym::end => Some(Range { + start: None, + end: Some(field.expr), + limits: ast::RangeLimits::Closed, + span, + }), (hir::LangItem::RangeTo, [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 11220a8c4c017..4c0d70d320b9f 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -342,7 +342,7 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)), QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath), - _ => false, + QPath::TypeRelative(..) => false, } } @@ -782,7 +782,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => { capture = CaptureKind::Value; }, - ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => { + ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => { capture = CaptureKind::Ref(Mutability::Mut); }, _ => (), @@ -1830,7 +1830,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< .typeck_results() .pat_binding_modes() .get(pat.hir_id) - .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) + .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..))) { // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then // due to match ergonomics, the inner patterns become references. Don't consider this diff --git a/src/tools/clippy/clippy_utils/src/res.rs b/src/tools/clippy/clippy_utils/src/res.rs index d1f6ebf35ce24..a3efece7d2245 100644 --- a/src/tools/clippy/clippy_utils/src/res.rs +++ b/src/tools/clippy/clippy_utils/src/res.rs @@ -107,9 +107,7 @@ pub trait MaybeQPath<'a>: Copy { fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { match *qpath { QPath::Resolved(_, p) => p.res, - QPath::TypeRelative(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { - Res::Def(kind, id) - }, + QPath::TypeRelative(..) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), QPath::TypeRelative(..) => Res::Err, } } @@ -403,7 +401,7 @@ impl<'a> MaybeResPath<'a> for &QPath<'a> { fn opt_res_path(self) -> OptResPath<'a> { match *self { QPath::Resolved(ty, path) => (ty, Some(path)), - _ => (None, None), + QPath::TypeRelative(..) => (None, None), } } } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 954a71f6c320e..e29d45551d1b0 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DesugaringKind, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, + BytePos, DUMMY_SP, DesugaringKind, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, hygiene, }; use std::borrow::Cow; @@ -675,7 +675,7 @@ fn snippet_with_context_sess<'a>( return ( snippet_with_applicability_sess(sess, span, default, applicability), false, - ) + ); } let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 5945a0c7b5587..2593df1035277 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -127,7 +127,11 @@ impl<'a> Sugg<'a> { /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { + fn hir_from_snippet( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + mut get_snippet: impl FnMut(Span) -> Cow<'a, str>, + ) -> Self { if let Some(range) = higher::Range::hir(cx, expr) { let op = AssocOp::Range(range.limits); let start = range.start.map_or("".into(), |expr| get_snippet(expr.span)); @@ -765,7 +769,7 @@ pub struct DerefClosure { /// such as explicit deref and borrowing cases. /// Returns `None` if no such use cases have been triggered in closure body /// -/// note: this only works on single line immutable closures with exactly one input parameter. +/// note: This only works on immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { if let ExprKind::Closure(&Closure { fn_decl, def_id, body, .. diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 4de7b5fb5924d..b73a7c7bb4d98 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.92" +version = "0.1.93" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index d5d96448a97d3..d23fd74d9accd 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-16" +channel = "nightly-2025-10-31" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 205cd8ba4ee88..001a6ceb1b17d 100644 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -9,7 +9,7 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::never_loop, - clippy::needless_if, + clippy::needless_ifs, clippy::collapsible_if, clippy::blocks_in_conditions, clippy::single_match, diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 9efbb39084976..1f1afe4ea3a53 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -57,3 +57,15 @@ macro_rules! bad_transmute { std::mem::transmute($e) }; } + +#[macro_export] +#[rustfmt::skip] +macro_rules! double_parens { + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (()); + let b = ((5)); + let c = std::convert::identity((5)); + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 5465092287179..629a500ff6f56 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -230,3 +230,14 @@ pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream { span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()), ]) } + +#[proc_macro_derive(DoubleParens)] +pub fn derive_double_parens(_: TokenStream) -> TokenStream { + quote! { + fn foo() { + let a = (()); + let b = ((5)); + let c = std::convert::identity((5)); + } + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index bb55539617fc5..a7c20a787519f 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -1,5 +1,5 @@ #![feature(proc_macro_span)] -#![allow(clippy::needless_if, dead_code)] +#![allow(clippy::needless_ifs, dead_code)] extern crate proc_macro; diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index 6ae5b0cb2f041..417ed370e7e5b 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -4,7 +4,7 @@ #![allow( unused, unnecessary_transmutes, - clippy::needless_if, + clippy::needless_ifs, clippy::missing_transmute_annotations )] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 3fd060620728f..fd67ad372114d 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -4,7 +4,7 @@ #![allow( unused, unnecessary_transmutes, - clippy::needless_if, + clippy::needless_ifs, clippy::missing_transmute_annotations )] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed index b0b60104c0b91..52564c1cb0922 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(non_local_definitions, clippy::needless_if)] +#![allow(non_local_definitions, clippy::needless_ifs)] #![warn(clippy::bool_comparison)] #![allow(clippy::non_canonical_partial_ord_impl)] diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs index 1b1108d6ce504..a78cf167e84b2 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_comparison.rs @@ -1,4 +1,4 @@ -#![allow(non_local_definitions, clippy::needless_if)] +#![allow(non_local_definitions, clippy::needless_ifs)] #![warn(clippy::bool_comparison)] #![allow(clippy::non_canonical_partial_ord_impl)] diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed index 8d543b0422009..2e57078d8e169 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed +++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_clone, clippy::derive_partial_eq_without_eq )] // See #5700 diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs index 6da311c50ee7b..7040d8a41030d 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_clone, clippy::derive_partial_eq_without_eq )] // See #5700 diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed index da958f76a5ca0..e7439beef186c 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_ifs)] #![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs index 06af49f2f6f3b..434ba3654f983 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_ifs)] #![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index ca9d02ff2d4f8..6e3fd0f78ddfd 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -1,7 +1,7 @@ #![allow( clippy::assertions_on_constants, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::nonminimal_bool, clippy::eq_op, clippy::redundant_pattern_matching diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index 9ac68ecd4cac0..666252a526547 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -1,7 +1,7 @@ #![allow( clippy::assertions_on_constants, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::nonminimal_bool, clippy::eq_op, clippy::redundant_pattern_matching diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed index 7a71829dd62cc..4c3b6004e8006 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed +++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed @@ -1,5 +1,5 @@ #![warn(clippy::comparison_to_empty)] -#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_ifs, clippy::useless_vec)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs index 5d213a09e8126..0d4bbe4abf8a0 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.rs +++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs @@ -1,5 +1,5 @@ #![warn(clippy::comparison_to_empty)] -#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_ifs, clippy::useless_vec)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.fixed b/src/tools/clippy/tests/ui/crashes/ice-7169.fixed index 71a40ad7de71a..4392e5d4c028b 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7169.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-7169.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[derive(Default)] struct A { diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.rs b/src/tools/clippy/tests/ui/crashes/ice-7169.rs index d43e2cc164d75..a2aa1bdd5275e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7169.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7169.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[derive(Default)] struct A { diff --git a/src/tools/clippy/tests/ui/disallowed_names.rs b/src/tools/clippy/tests/ui/disallowed_names.rs index 15bb67349976d..331cccef9f932 100644 --- a/src/tools/clippy/tests/ui/disallowed_names.rs +++ b/src/tools/clippy/tests/ui/disallowed_names.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![allow( dead_code, - clippy::needless_if, + clippy::needless_ifs, clippy::similar_names, clippy::single_match, clippy::toplevel_ref_arg, diff --git a/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs b/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs index 8c3217624d443..0fbf29365ecfe 100644 --- a/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs +++ b/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs @@ -1,33 +1,14 @@ #![warn(clippy::needless_doctest_main)] -//! issue 10491: -//! ```rust,no_test -//! use std::collections::HashMap; -//! -//! fn main() { -//! let mut m = HashMap::new(); -//! m.insert(1u32, 2u32); -//! } -//! ``` -/// some description here -/// ```rust,no_test -/// fn main() { -/// foo() -/// } -/// ``` -fn foo() {} - -#[rustfmt::skip] /// Description /// ```rust /// fn main() { -//~^ error: needless `fn main` in doctest +//~^ needless_doctest_main /// let a = 0; /// } /// ``` fn mulpipulpi() {} -#[rustfmt::skip] /// With a `#[no_main]` /// ```rust /// #[no_main] @@ -45,7 +26,6 @@ fn pulpimulpi() {} /// ``` fn plumilupi() {} -#[rustfmt::skip] /// Additional function, shouldn't trigger /// ```rust /// fn additional_function() { @@ -58,7 +38,6 @@ fn plumilupi() {} /// ``` fn mlupipupi() {} -#[rustfmt::skip] /// Additional function AFTER main, shouldn't trigger /// ```rust /// fn main() { @@ -71,22 +50,19 @@ fn mlupipupi() {} /// ``` fn lumpimupli() {} -#[rustfmt::skip] /// Ignore code block, should not lint at all /// ```rust, ignore /// fn main() { -//~^ error: needless `fn main` in doctest /// // Hi! /// let _ = 0; /// } /// ``` fn mpulpilumi() {} -#[rustfmt::skip] /// Spaces in weird positions (including an \u{A0} after `main`) /// ```rust /// fn main (){ -//~^ error: needless `fn main` in doctest +//~^ needless_doctest_main /// let _ = 0; /// } /// ``` diff --git a/src/tools/clippy/tests/ui/doc/needless_doctest_main.stderr b/src/tools/clippy/tests/ui/doc/needless_doctest_main.stderr index dd5474ccb85af..693cc22fba2a7 100644 --- a/src/tools/clippy/tests/ui/doc/needless_doctest_main.stderr +++ b/src/tools/clippy/tests/ui/doc/needless_doctest_main.stderr @@ -1,36 +1,17 @@ error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:23:5 + --> tests/ui/doc/needless_doctest_main.rs:5:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// let a = 0; -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:77:5 + --> tests/ui/doc/needless_doctest_main.rs:64:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// // Hi! -LL | | /// let _ = 0; -LL | | /// } - | |_____^ +LL | /// fn main (){ + | ^^^^^^^^^^^ -error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:88:5 - | -LL | /// fn main (){ - | _____^ -LL | | -LL | | /// let _ = 0; -LL | | /// } - | |_____^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/double_comparison.fixed b/src/tools/clippy/tests/ui/double_comparison.fixed index 685e3319bf9af..0680eb35ef974 100644 --- a/src/tools/clippy/tests/ui/double_comparison.fixed +++ b/src/tools/clippy/tests/ui/double_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn main() { let x = 1; diff --git a/src/tools/clippy/tests/ui/double_comparison.rs b/src/tools/clippy/tests/ui/double_comparison.rs index 3670a050e88d1..18ab7d2c42546 100644 --- a/src/tools/clippy/tests/ui/double_comparison.rs +++ b/src/tools/clippy/tests/ui/double_comparison.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn main() { let x = 1; diff --git a/src/tools/clippy/tests/ui/double_parens.fixed b/src/tools/clippy/tests/ui/double_parens.fixed index dedc513438d11..024af68401327 100644 --- a/src/tools/clippy/tests/ui/double_parens.fixed +++ b/src/tools/clippy/tests/ui/double_parens.fixed @@ -1,8 +1,13 @@ +//@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs +//@aux-build:macro_rules.rs #![warn(clippy::double_parens)] #![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +use proc_macros::{external, with_span}; + fn dummy_fn(_: T) {} struct DummyStruct; @@ -96,4 +101,64 @@ fn issue9000(x: DummyStruct) { //~^ double_parens } +fn issue15892() { + use macro_rules::double_parens as double_parens_external; + + macro_rules! double_parens{ + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (); + //~^ double_parens + let b = (5); + //~^ double_parens + let c = std::convert::identity(5); + //~^ double_parens + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; + } + + // Don't lint: external macro + (external!((5))); + external!(((5))); + + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct InterruptMask(u32); + + impl InterruptMask { + pub const OE: InterruptMask = InterruptMask(1 << 10); + pub const BE: InterruptMask = InterruptMask(1 << 9); + pub const PE: InterruptMask = InterruptMask(1 << 8); + pub const FE: InterruptMask = InterruptMask(1 << 7); + // Lint: internal macro + pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + // Don't lint: external macro + pub const F: InterruptMask = double_parens_external!((Self::OE), Self::BE, Self::PE, Self::FE); + #[allow(clippy::unnecessary_cast)] + pub const G: InterruptMask = external!( + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + #[allow(clippy::unnecessary_cast)] + // Don't lint: external proc-macro + pub const H: InterruptMask = with_span!(span + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + pub const fn into_bits(self) -> u32 { + self.0 + } + #[must_use] + pub const fn union(self, rhs: Self) -> Self { + InterruptMask(self.0 | rhs.0) + } + } +} + +fn issue15940() { + use proc_macro_derive::DoubleParens; + + #[derive(DoubleParens)] + // Don't lint: external derive macro + pub struct Person; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/double_parens.rs b/src/tools/clippy/tests/ui/double_parens.rs index 27f252485b714..8a76f2837f353 100644 --- a/src/tools/clippy/tests/ui/double_parens.rs +++ b/src/tools/clippy/tests/ui/double_parens.rs @@ -1,8 +1,13 @@ +//@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs +//@aux-build:macro_rules.rs #![warn(clippy::double_parens)] #![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +use proc_macros::{external, with_span}; + fn dummy_fn(_: T) {} struct DummyStruct; @@ -96,4 +101,64 @@ fn issue9000(x: DummyStruct) { //~^ double_parens } +fn issue15892() { + use macro_rules::double_parens as double_parens_external; + + macro_rules! double_parens{ + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (()); + //~^ double_parens + let b = ((5)); + //~^ double_parens + let c = std::convert::identity((5)); + //~^ double_parens + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; + } + + // Don't lint: external macro + (external!((5))); + external!(((5))); + + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct InterruptMask(u32); + + impl InterruptMask { + pub const OE: InterruptMask = InterruptMask(1 << 10); + pub const BE: InterruptMask = InterruptMask(1 << 9); + pub const PE: InterruptMask = InterruptMask(1 << 8); + pub const FE: InterruptMask = InterruptMask(1 << 7); + // Lint: internal macro + pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + // Don't lint: external macro + pub const F: InterruptMask = double_parens_external!((Self::OE), Self::BE, Self::PE, Self::FE); + #[allow(clippy::unnecessary_cast)] + pub const G: InterruptMask = external!( + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + #[allow(clippy::unnecessary_cast)] + // Don't lint: external proc-macro + pub const H: InterruptMask = with_span!(span + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + pub const fn into_bits(self) -> u32 { + self.0 + } + #[must_use] + pub const fn union(self, rhs: Self) -> Self { + InterruptMask(self.0 | rhs.0) + } + } +} + +fn issue15940() { + use proc_macro_derive::DoubleParens; + + #[derive(DoubleParens)] + // Don't lint: external derive macro + pub struct Person; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/double_parens.stderr b/src/tools/clippy/tests/ui/double_parens.stderr index 3a740e44cacf3..51b5c6279b211 100644 --- a/src/tools/clippy/tests/ui/double_parens.stderr +++ b/src/tools/clippy/tests/ui/double_parens.stderr @@ -1,5 +1,5 @@ error: unnecessary parentheses - --> tests/ui/double_parens.rs:15:5 + --> tests/ui/double_parens.rs:20:5 | LL | ((0)) | ^^^^^ help: remove them: `(0)` @@ -8,37 +8,37 @@ LL | ((0)) = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` error: unnecessary parentheses - --> tests/ui/double_parens.rs:20:14 + --> tests/ui/double_parens.rs:25:14 | LL | dummy_fn((0)); | ^^^ help: remove them: `0` error: unnecessary parentheses - --> tests/ui/double_parens.rs:25:20 + --> tests/ui/double_parens.rs:30:20 | LL | x.dummy_method((0)); | ^^^ help: remove them: `0` error: unnecessary parentheses - --> tests/ui/double_parens.rs:30:5 + --> tests/ui/double_parens.rs:35:5 | LL | ((1, 2)) | ^^^^^^^^ help: remove them: `(1, 2)` error: unnecessary parentheses - --> tests/ui/double_parens.rs:36:5 + --> tests/ui/double_parens.rs:41:5 | LL | (()) | ^^^^ help: remove them: `()` error: unnecessary parentheses - --> tests/ui/double_parens.rs:59:16 + --> tests/ui/double_parens.rs:64:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ help: remove them: `(1, 2)` error: unnecessary parentheses - --> tests/ui/double_parens.rs:84:16 + --> tests/ui/double_parens.rs:89:16 | LL | () => {((100))} | ^^^^^^^ help: remove them: `(100)` @@ -49,22 +49,55 @@ LL | bar!(); = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary parentheses - --> tests/ui/double_parens.rs:91:5 + --> tests/ui/double_parens.rs:96:5 | LL | ((vec![1, 2])); | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` error: unnecessary parentheses - --> tests/ui/double_parens.rs:93:14 + --> tests/ui/double_parens.rs:98:14 | LL | dummy_fn((vec![1, 2])); | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` error: unnecessary parentheses - --> tests/ui/double_parens.rs:95:20 + --> tests/ui/double_parens.rs:100:20 | LL | x.dummy_method((vec![1, 2])); | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` -error: aborting due to 10 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:110:21 + | +LL | let a = (()); + | ^^^^ help: remove them: `()` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:112:21 + | +LL | let b = ((5)); + | ^^^^^ help: remove them: `(5)` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:114:44 + | +LL | let c = std::convert::identity((5)); + | ^^^ help: remove them: `5` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs deleted file mode 100644 index 439fd0974f5f8..0000000000000 --- a/src/tools/clippy/tests/ui/empty_enum.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![allow(dead_code)] -#![warn(clippy::empty_enum)] -// Enable never type to test empty enum lint -#![feature(never_type)] -enum Empty {} -//~^ empty_enum - -fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enums.rs b/src/tools/clippy/tests/ui/empty_enums.rs new file mode 100644 index 0000000000000..0deb0f57b0e4a --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enums.rs @@ -0,0 +1,25 @@ +#![warn(clippy::empty_enums)] +// Enable never type to test empty enum lint +#![feature(never_type)] + +enum Empty {} +//~^ empty_enums + +mod issue15910 { + enum NotReallyEmpty { + #[cfg(false)] + Hidden, + } + + enum OneVisibleVariant { + #[cfg(false)] + Hidden, + Visible, + } + + enum CfgInsideVariant { + Variant(#[cfg(false)] String), + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enums.stderr similarity index 75% rename from src/tools/clippy/tests/ui/empty_enum.stderr rename to src/tools/clippy/tests/ui/empty_enums.stderr index 6a1ded9298ed7..5aa2347b4ae08 100644 --- a/src/tools/clippy/tests/ui/empty_enum.stderr +++ b/src/tools/clippy/tests/ui/empty_enums.stderr @@ -1,12 +1,12 @@ error: enum with no variants - --> tests/ui/empty_enum.rs:5:1 + --> tests/ui/empty_enums.rs:5:1 | LL | enum Empty {} | ^^^^^^^^^^^^^ | = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated - = note: `-D clippy::empty-enum` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_enum)]` + = note: `-D clippy::empty-enums` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_enums)]` error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs b/src/tools/clippy/tests/ui/empty_enums_without_never_type.rs similarity index 67% rename from src/tools/clippy/tests/ui/empty_enum_without_never_type.rs rename to src/tools/clippy/tests/ui/empty_enums_without_never_type.rs index 3661a15372086..17ccac83ce98e 100644 --- a/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs +++ b/src/tools/clippy/tests/ui/empty_enums_without_never_type.rs @@ -1,7 +1,6 @@ //@ check-pass -#![allow(dead_code)] -#![warn(clippy::empty_enum)] +#![warn(clippy::empty_enums)] // `never_type` is not enabled; this test has no stderr file enum Empty {} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed index ce8b67f9ca7b0..58fbad64a78da 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.fixed +++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed @@ -4,7 +4,7 @@ unused_variables, dead_code, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #![warn(clippy::equatable_if_let)] diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs index ff09533f26519..cca97c76b5094 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.rs +++ b/src/tools/clippy/tests/ui/equatable_if_let.rs @@ -4,7 +4,7 @@ unused_variables, dead_code, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #![warn(clippy::equatable_if_let)] diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs index 2295691c81272..82ac4db172d88 100644 --- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs @@ -10,7 +10,7 @@ //! This test can't cover every lint from Clippy, rustdoc and potentially other //! tools that will be developed. This therefore only tests a small subset of lints #![expect(rustdoc::missing_crate_level_docs)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] mod rustc_ok { //! See diff --git a/src/tools/clippy/tests/ui/explicit_write_in_test.stderr b/src/tools/clippy/tests/ui/explicit_write_in_test.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/tools/clippy/tests/ui/filetype_is_file.rs b/src/tools/clippy/tests/ui/filetype_is_file.rs index 8ca01b91210fb..2ec5b3b81446d 100644 --- a/src/tools/clippy/tests/ui/filetype_is_file.rs +++ b/src/tools/clippy/tests/ui/filetype_is_file.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #![warn(clippy::filetype_is_file)] fn main() -> std::io::Result<()> { diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs index 5b74aecdacbe5..6ac5fe6e7b543 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -5,7 +5,7 @@ clippy::equatable_if_let, clippy::collapsible_if, clippy::ifs_same_cond, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::single_element_loop, clippy::branches_sharing_code diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs index 7067434953d6e..486903f653de6 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::needless_if, clippy::needless_else)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::needless_ifs, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs index 15cb61c6eebd9..e6044cc507812 100644 --- a/src/tools/clippy/tests/ui/impl.rs +++ b/src/tools/clippy/tests/ui/impl.rs @@ -84,4 +84,26 @@ impl OneExpected {} impl OneExpected {} //~^ multiple_inherent_impl +// issue #8714 +struct Lifetime<'s> { + s: &'s str, +} + +impl Lifetime<'_> {} +impl Lifetime<'_> {} // false negative + +impl<'a> Lifetime<'a> {} +impl<'a> Lifetime<'a> {} // false negative + +impl<'b> Lifetime<'b> {} // false negative? + +impl Lifetime<'static> {} + +struct Generic { + g: Vec, +} + +impl Generic {} +impl Generic {} // false negative + fn main() {} diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index f7f21e1850d0a..3069c8139abe3 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -168,4 +168,14 @@ fn enum_variant_ok() { let _ = const { std::io::ErrorKind::InvalidFilename }; } +#[clippy::msrv = "1.38.0"] +const fn uncalled_len() { + let _ = Vec::::len; + let x = str::len; + let _ = x(""); + //~^ incompatible_msrv + let _ = "".len(); + //~^ incompatible_msrv +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.stderr b/src/tools/clippy/tests/ui/incompatible_msrv.stderr index e42360d296f58..3c0bb595bd5b0 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.stderr +++ b/src/tools/clippy/tests/ui/incompatible_msrv.stderr @@ -110,5 +110,17 @@ error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item i LL | let _ = const { std::io::ErrorKind::InvalidFilename }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.38.0` but this item is stable in a `const` context since `1.39.0` + --> tests/ui/incompatible_msrv.rs:175:13 + | +LL | let _ = x(""); + | ^ + +error: current MSRV (Minimum Supported Rust Version) is `1.38.0` but this item is stable in a `const` context since `1.39.0` + --> tests/ui/incompatible_msrv.rs:177:16 + | +LL | let _ = "".len(); + | ^^^^^ + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr b/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr index ea9f0e716c7d4..32a61a8585f03 100644 --- a/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr +++ b/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr @@ -1,4 +1,4 @@ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:10:14 | LL | Self(self.0 / rhs.0) @@ -7,49 +7,49 @@ LL | Self(self.0 / rhs.0) = note: `-D clippy::integer-division-remainder-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::integer_division_remainder_used)]` -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:18:14 | LL | Self(self.0 % rhs.0) | ^^^^^^^^^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:27:13 | LL | let c = a / b; | ^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:29:13 | LL | let d = a % b; | ^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:31:13 | LL | let e = &a / b; | ^^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:33:13 | LL | let f = a % &b; | ^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:35:13 | LL | let g = &a / &b; | ^^^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:37:13 | LL | let h = &10 % b; | ^^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:39:13 | LL | let i = a / &4; diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index b8573ef13b0e8..50b96f7712d71 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -2,7 +2,7 @@ #![allow( dead_code, unused, - clippy::needless_if, + clippy::needless_ifs, clippy::len_without_is_empty, clippy::const_is_empty )] @@ -275,3 +275,7 @@ fn no_infinite_recursion() -> bool { // Do not crash while checking if S implements `.is_empty()` S == "" } + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index ef3c49c1ab309..a6acc2be714bc 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -2,7 +2,7 @@ #![allow( dead_code, unused, - clippy::needless_if, + clippy::needless_ifs, clippy::len_without_is_empty, clippy::const_is_empty )] @@ -275,3 +275,7 @@ fn no_infinite_recursion() -> bool { // Do not crash while checking if S implements `.is_empty()` S == "" } + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 +} diff --git a/src/tools/clippy/tests/ui/len_zero_unstable.fixed b/src/tools/clippy/tests/ui/len_zero_unstable.fixed new file mode 100644 index 0000000000000..8d4e6c2cc006f --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_unstable.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::len_zero)] +#![feature(exact_size_is_empty)] + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.is_empty() + //~^ len_zero +} diff --git a/src/tools/clippy/tests/ui/len_zero_unstable.rs b/src/tools/clippy/tests/ui/len_zero_unstable.rs new file mode 100644 index 0000000000000..f59056c5c55b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_unstable.rs @@ -0,0 +1,7 @@ +#![warn(clippy::len_zero)] +#![feature(exact_size_is_empty)] + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 + //~^ len_zero +} diff --git a/src/tools/clippy/tests/ui/len_zero_unstable.stderr b/src/tools/clippy/tests/ui/len_zero_unstable.stderr new file mode 100644 index 0000000000000..103ccf3dcbf5a --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_unstable.stderr @@ -0,0 +1,11 @@ +error: length comparison to zero + --> tests/ui/len_zero_unstable.rs:5:5 + | +LL | vertices.len() == 0 + | ^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `vertices.is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::len_zero)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed index 977e31c744a2c..0617f06fafb7c 100644 --- a/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed +++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed @@ -1,39 +1,37 @@ -#![allow(unused, clippy::map_identity)] +#![allow(clippy::map_identity)] #![warn(clippy::lines_filter_map_ok)] use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { + // Lint: + let f = std::fs::File::open("/")?; - // Lint BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - let s = "foo\nbar\nbaz\n"; - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Do not lint (not a `Lines` iterator) + + // Do not lint: + + // not a `Lines` iterator io::stdin() .lines() .map(std::convert::identity) .filter_map(|x| x.ok()) .for_each(|_| ()); - // Do not lint (not a `Result::ok()` extractor) + // not a `Result::ok()` extractor io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ()); Ok(()) } diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.rs b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs index 2196075bc4455..dfd1ec431a52f 100644 --- a/src/tools/clippy/tests/ui/lines_filter_map_ok.rs +++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs @@ -1,39 +1,37 @@ -#![allow(unused, clippy::map_identity)] +#![allow(clippy::map_identity)] #![warn(clippy::lines_filter_map_ok)] use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { + // Lint: + let f = std::fs::File::open("/")?; - // Lint BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().flatten().for_each(|_| ()); //~^ lines_filter_map_ok - let s = "foo\nbar\nbaz\n"; - // Lint io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().flatten().for_each(|_| ()); //~^ lines_filter_map_ok - // Do not lint (not a `Lines` iterator) + + // Do not lint: + + // not a `Lines` iterator io::stdin() .lines() .map(std::convert::identity) .filter_map(|x| x.ok()) .for_each(|_| ()); - // Do not lint (not a `Result::ok()` extractor) + // not a `Result::ok()` extractor io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ()); Ok(()) } diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr index f9038eec9fb21..c05dadd1b5f29 100644 --- a/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr +++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr @@ -1,11 +1,11 @@ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:9:31 + --> tests/ui/lines_filter_map_ok.rs:10:31 | LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:9:5 + --> tests/ui/lines_filter_map_ok.rs:10:5 | LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,49 +25,49 @@ LL | BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `flatten()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:17:31 + --> tests/ui/lines_filter_map_ok.rs:16:31 | LL | BufReader::new(f).lines().flatten().for_each(|_| ()); | ^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:17:5 + --> tests/ui/lines_filter_map_ok.rs:16:5 | LL | BufReader::new(f).lines().flatten().for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:22:25 + --> tests/ui/lines_filter_map_ok.rs:19:25 | LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:22:5 + --> tests/ui/lines_filter_map_ok.rs:19:5 | LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:25:25 + --> tests/ui/lines_filter_map_ok.rs:21:25 | LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:25:5 + --> tests/ui/lines_filter_map_ok.rs:21:5 | LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ error: `flatten()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:28:25 + --> tests/ui/lines_filter_map_ok.rs:23:25 | LL | io::stdin().lines().flatten().for_each(|_| ()); | ^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:28:5 + --> tests/ui/lines_filter_map_ok.rs:23:5 | LL | io::stdin().lines().flatten().for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_float_methods.rs b/src/tools/clippy/tests/ui/manual_float_methods.rs index 4b496a4932833..3cd469fd43a0d 100644 --- a/src/tools/clippy/tests/ui/manual_float_methods.rs +++ b/src/tools/clippy/tests/ui/manual_float_methods.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions //@aux-build:proc_macros.rs -#![allow(clippy::needless_if, unused)] +#![allow(clippy::needless_ifs, unused)] #![warn(clippy::manual_is_infinite, clippy::manual_is_finite)] // FIXME(f16_f128): add tests for these types once constants are available diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index 3781ba1676f5e..4523edec3c764 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -6,7 +6,7 @@ clippy::let_unit_value, clippy::match_single_binding, clippy::never_loop, - clippy::needless_if, + clippy::needless_ifs, clippy::diverging_sub_expression, clippy::single_match, clippy::manual_unwrap_or_default @@ -546,3 +546,28 @@ mod issue14598 { todo!() } } + +mod issue15914 { + // https://github.com/rust-lang/rust-clippy/issues/15914 + unsafe fn something_unsafe() -> Option { + None + } + + fn foo() { + let value = if let Some(value) = unsafe { something_unsafe() } { + //~^ manual_let_else + value + } else { + return; + }; + + let some_flag = true; + + let value = if let Some(value) = if some_flag { None } else { Some(3) } { + //~^ manual_let_else + value + } else { + return; + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index a1eea04192919..f4b1644c44baa 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -549,5 +549,41 @@ LL | | Some(x) => x, LL | | }; | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` -error: aborting due to 35 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:557:9 + | +LL | / let value = if let Some(value) = unsafe { something_unsafe() } { +LL | | +LL | | value +LL | | } else { +LL | | return; +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Some(value) = (unsafe { something_unsafe() }) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:566:9 + | +LL | / let value = if let Some(value) = if some_flag { None } else { Some(3) } { +LL | | +LL | | value +LL | | } else { +LL | | return; +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Some(value) = (if some_flag { None } else { Some(3) }) else { +LL + return; +LL + }; + | + +error: aborting due to 37 previous errors diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.stderr b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr index e240ae8eb7d90..37113e9eafe1b 100644 --- a/src/tools/clippy/tests/ui/manual_option_as_slice.stderr +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr @@ -1,4 +1,4 @@ -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:5:9 | LL | _ = match x.as_ref() { @@ -7,12 +7,21 @@ LL | | LL | | Some(f) => std::slice::from_ref(f), LL | | None => &[], LL | | }; - | |_____^ help: use: `x.as_slice()` + | |_____^ | = note: `-D clippy::manual-option-as-slice` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]` +help: use `Option::as_slice` directly + | +LL - _ = match x.as_ref() { +LL - +LL - Some(f) => std::slice::from_ref(f), +LL - None => &[], +LL - }; +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:11:9 | LL | _ = if let Some(f) = x.as_ref() { @@ -23,37 +32,79 @@ LL | | std::slice::from_ref(f) LL | | } else { LL | | &[] LL | | }; - | |_____^ help: use: `x.as_slice()` + | |_____^ + | +help: use `Option::as_slice` directly + | +LL - _ = if let Some(f) = x.as_ref() { +LL - +LL - +LL - std::slice::from_ref(f) +LL - } else { +LL - &[] +LL - }; +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:19:9 | LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:22:9 | LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:25:9 | LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:28:9 | LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:33:13 | LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); +LL + _ = x.as_slice(); + | error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index e12287a709395..8dd9e7a8a6fae 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -250,4 +250,18 @@ fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or(0) } +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 2 }; + + let x = uncopyable_res; + let _ = x.unwrap_or(2); + //~^ manual_unwrap_or + + let copyable_res: Result = Ok(1); + let _ = copyable_res.unwrap_or(2); + //~^ manual_unwrap_or + let _ = copyable_res; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index 53cffcab5b56c..f8e2c5f43745a 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -329,4 +329,18 @@ fn allowed_manual_unwrap_or_zero() -> u32 { } } +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 2 }; + + let x = uncopyable_res; + let _ = if let Ok(v) = x { v } else { 2 }; + //~^ manual_unwrap_or + + let copyable_res: Result = Ok(1); + let _ = if let Ok(v) = copyable_res { v } else { 2 }; + //~^ manual_unwrap_or + let _ = copyable_res; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr index 320e895fb8237..18764d1501d89 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr @@ -201,5 +201,17 @@ LL | | 0 LL | | } | |_____^ help: replace with: `Some(42).unwrap_or(0)` -error: aborting due to 18 previous errors +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:337:13 + | +LL | let _ = if let Ok(v) = x { v } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `x.unwrap_or(2)` + +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:341:13 + | +LL | let _ = if let Ok(v) = copyable_res { v } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `copyable_res.unwrap_or(2)` + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index 11023ac1142a7..0c64ed1826b26 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -128,3 +128,17 @@ mod issue14716 { }; } } + +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 0 }; + + let x = uncopyable_res; + let _ = x.unwrap_or_default(); + //~^ manual_unwrap_or_default + + let copyable_res: Result = Ok(1); + let _ = copyable_res.unwrap_or_default(); + //~^ manual_unwrap_or_default + let _ = copyable_res; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index bf06d9af7d219..e99f7d44dde19 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -169,3 +169,17 @@ mod issue14716 { }; } } + +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 0 }; + + let x = uncopyable_res; + let _ = if let Ok(v) = x { v } else { 0 }; + //~^ manual_unwrap_or_default + + let copyable_res: Result = Ok(1); + let _ = if let Ok(v) = copyable_res { v } else { 0 }; + //~^ manual_unwrap_or_default + let _ = copyable_res; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index 031100832b16f..a9e78afe6f065 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -97,5 +97,17 @@ LL | | 0 LL | | } | |_____^ help: replace it with: `Some(42).unwrap_or_default()` -error: aborting due to 9 previous errors +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:178:13 + | +LL | let _ = if let Ok(v) = x { v } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:182:13 + | +LL | let _ = if let Ok(v) = copyable_res { v } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `copyable_res.unwrap_or_default()` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed index a39f0c9299bd5..09a6ed1693905 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.fixed +++ b/src/tools/clippy/tests/ui/match_as_ref.fixed @@ -71,3 +71,22 @@ mod issue15691 { }; } } + +fn recv_requiring_parens() { + struct S; + + impl std::ops::Not for S { + type Output = Option; + fn not(self) -> Self::Output { + None + } + } + + let _ = (!S).as_ref(); +} + +fn issue15932() { + let _: Option<&u32> = Some(0).as_ref(); + + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); +} diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs index 049928167901a..347b6d1868879 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.rs +++ b/src/tools/clippy/tests/ui/match_as_ref.rs @@ -83,3 +83,34 @@ mod issue15691 { }; } } + +fn recv_requiring_parens() { + struct S; + + impl std::ops::Not for S { + type Output = Option; + fn not(self) -> Self::Output { + None + } + } + + let _ = match !S { + //~^ match_as_ref + None => None, + Some(ref v) => Some(v), + }; +} + +fn issue15932() { + let _: Option<&u32> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; + + let _: Option<&dyn std::fmt::Debug> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; +} diff --git a/src/tools/clippy/tests/ui/match_as_ref.stderr b/src/tools/clippy/tests/ui/match_as_ref.stderr index 7b40cb80dc6ed..df06e358f296e 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.stderr +++ b/src/tools/clippy/tests/ui/match_as_ref.stderr @@ -1,4 +1,4 @@ -error: use `as_ref()` instead +error: manual implementation of `Option::as_ref` --> tests/ui/match_as_ref.rs:6:33 | LL | let borrowed: Option<&()> = match owned { @@ -7,12 +7,21 @@ LL | | LL | | None => None, LL | | Some(ref v) => Some(v), LL | | }; - | |_____^ help: try: `owned.as_ref()` + | |_____^ | = note: `-D clippy::match-as-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_as_ref)]` +help: use `Option::as_ref()` directly + | +LL - let borrowed: Option<&()> = match owned { +LL - +LL - None => None, +LL - Some(ref v) => Some(v), +LL - }; +LL + let borrowed: Option<&()> = owned.as_ref(); + | -error: use `as_mut()` instead +error: manual implementation of `Option::as_mut` --> tests/ui/match_as_ref.rs:13:39 | LL | let borrow_mut: Option<&mut ()> = match mut_owned { @@ -21,9 +30,19 @@ LL | | LL | | None => None, LL | | Some(ref mut v) => Some(v), LL | | }; - | |_____^ help: try: `mut_owned.as_mut()` + | |_____^ + | +help: use `Option::as_mut()` directly + | +LL - let borrow_mut: Option<&mut ()> = match mut_owned { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let borrow_mut: Option<&mut ()> = mut_owned.as_mut(); + | -error: use `as_ref()` instead +error: manual implementation of `Option::as_ref` --> tests/ui/match_as_ref.rs:32:13 | LL | / match self.source { @@ -31,7 +50,82 @@ LL | | LL | | Some(ref s) => Some(s), LL | | None => None, LL | | } - | |_____________^ help: try: `self.source.as_ref().map(|x| x as _)` + | |_____________^ + | +help: use `Option::as_ref()` directly + | +LL - match self.source { +LL - +LL - Some(ref s) => Some(s), +LL - None => None, +LL - } +LL + self.source.as_ref().map(|x| x as _) + | + +error: manual implementation of `Option::as_ref` + --> tests/ui/match_as_ref.rs:97:13 + | +LL | let _ = match !S { + | _____________^ +LL | | +LL | | None => None, +LL | | Some(ref v) => Some(v), +LL | | }; + | |_____^ + | +help: use `Option::as_ref()` directly + | +LL - let _ = match !S { +LL - +LL - None => None, +LL - Some(ref v) => Some(v), +LL - }; +LL + let _ = (!S).as_ref(); + | + +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:105:27 + | +LL | let _: Option<&u32> = match Some(0) { + | ___________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | + = note: but the type is coerced to a non-mutable reference, and so `as_ref` can used instead +help: use `Option::as_ref()` + | +LL - let _: Option<&u32> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&u32> = Some(0).as_ref(); + | + +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:111:43 + | +LL | let _: Option<&dyn std::fmt::Debug> = match Some(0) { + | ___________________________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | + = note: but the type is coerced to a non-mutable reference, and so `as_ref` can used instead +help: use `Option::as_ref()` + | +LL - let _: Option<&dyn std::fmt::Debug> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); + | -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs index 176287e3d2e0c..224cac055f251 100644 --- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs @@ -1,6 +1,6 @@ #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_ifs)] fn overlapping() { const FOO: u64 = 2; diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.normal.stderr b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.normal.stderr new file mode 100644 index 0000000000000..b7d95fbfa8f58 --- /dev/null +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.normal.stderr @@ -0,0 +1,31 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:11:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:3:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs new file mode 100644 index 0000000000000..15c8b7c508781 --- /dev/null +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs @@ -0,0 +1,46 @@ +//@compile-flags: --cfg test +#![deny(clippy::multiple_inherent_impl)] + +// issue #13040 + +fn main() {} + +struct A; + +impl A {} + +impl A {} +//~^ multiple_inherent_impl + +#[cfg(test)] +impl A {} // false positive +//~^ multiple_inherent_impl + +#[cfg(test)] +impl A {} +//~^ multiple_inherent_impl + +struct B; + +impl B {} + +#[cfg(test)] +impl B {} // false positive +//~^ multiple_inherent_impl + +impl B {} +//~^ multiple_inherent_impl + +#[cfg(test)] +impl B {} +//~^ multiple_inherent_impl + +#[cfg(test)] +struct C; + +#[cfg(test)] +impl C {} + +#[cfg(test)] +impl C {} +//~^ multiple_inherent_impl diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr new file mode 100644 index 0000000000000..9d408ce3dec38 --- /dev/null +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr @@ -0,0 +1,91 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:12:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:2:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:16:1 + | +LL | impl A {} // false positive + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:20:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:28:1 + | +LL | impl B {} // false positive + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:31:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:35:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:45:1 + | +LL | impl C {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:42:1 + | +LL | impl C {} + | ^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.withtest.stderr b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.withtest.stderr new file mode 100644 index 0000000000000..1e98b1f18801e --- /dev/null +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.withtest.stderr @@ -0,0 +1,91 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:11:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:3:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:14:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:17:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:23:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:28:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:36:1 + | +LL | impl C {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:34:1 + | +LL | impl C {} + | ^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 0664abf0944dc..2589819f019b8 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::self_named_constructors, clippy::struct_field_names diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index 7507a6af408bb..f9cc0122f5e7b 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::self_named_constructors, clippy::struct_field_names diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed index 84924cac62d52..6d74899218124 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed @@ -4,7 +4,7 @@ irrefutable_let_patterns, non_shorthand_field_patterns, clippy::needless_borrow, - clippy::needless_if + clippy::needless_ifs )] fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs index 280cef43340cc..a4cb899231647 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs @@ -4,7 +4,7 @@ irrefutable_let_patterns, non_shorthand_field_patterns, clippy::needless_borrow, - clippy::needless_if + clippy::needless_ifs )] fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index b09efe9888f50..842d77dbc8c50 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::suspicious_map, clippy::iter_count, clippy::manual_contains diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index da4182966bb17..98d8d27321d24 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::suspicious_map, clippy::iter_count, clippy::manual_contains diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs index fff6d2f34b8e3..69764becfe666 100644 --- a/src/tools/clippy/tests/ui/needless_collect_indirect.rs +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_if)] +#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_ifs)] #![warn(clippy::needless_collect)] //@no-rustfix use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs index cf01cae2e8fd4..1b6fa45be0fe5 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.rs +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -5,7 +5,7 @@ /// This should lint /// ``` /// fn main() { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -13,7 +13,7 @@ /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -21,7 +21,7 @@ /// This should, too. /// ```rust /// fn main() { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -29,8 +29,8 @@ /// This one too. /// ```no_run /// // the fn is not always the first line -//~^ ERROR: needless `fn main` in doctest /// fn main() { +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -38,12 +38,7 @@ fn bad_doctests() {} /// # Examples /// -/// This shouldn't lint, because the `main` is empty: -/// ``` -/// fn main(){} -/// ``` -/// -/// This shouldn't lint either, because main is async: +/// This shouldn't lint because main is async: /// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr index 9ba2ad306dac2..79763b0f22487 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.stderr +++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr @@ -1,12 +1,8 @@ error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:7:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` @@ -14,33 +10,20 @@ LL | | /// } error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:15:5 | -LL | /// fn main() -> () { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() -> () { + | ^^^^^^^ error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:23:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ error: needless `fn main` in doctest - --> tests/ui/needless_doc_main.rs:31:5 + --> tests/ui/needless_doc_main.rs:32:5 | -LL | /// // the fn is not always the first line - | _____^ -LL | | -LL | | /// fn main() { -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_ifs.fixed similarity index 82% rename from src/tools/clippy/tests/ui/needless_if.fixed rename to src/tools/clippy/tests/ui/needless_ifs.fixed index c839156bed9bd..0e0b0fa39c9b7 100644 --- a/src/tools/clippy/tests/ui/needless_if.fixed +++ b/src/tools/clippy/tests/ui/needless_ifs.fixed @@ -12,7 +12,7 @@ clippy::redundant_pattern_matching, unused )] -#![warn(clippy::needless_if)] +#![warn(clippy::needless_ifs)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -24,16 +24,16 @@ fn maybe_side_effect() -> bool { fn main() { // Lint - //~^ needless_if + //~^ needless_ifs // Do not remove the condition maybe_side_effect(); - //~^ needless_if + //~^ needless_ifs // Do not lint if (true) { } else { } ({ - //~^ needless_if + //~^ needless_ifs return; }); // Do not lint if `else if` is present @@ -48,7 +48,7 @@ fn main() { if true && let true = true {} // Can lint nested `if let`s ({ - //~^ needless_if + //~^ needless_ifs if let true = true && true { @@ -92,15 +92,24 @@ fn main() { // Must be placed into an expression context to not be interpreted as a block ({ maybe_side_effect() }); - //~^ needless_if + //~^ needless_ifs // Would be a block followed by `&&true` - a double reference to `true` ({ maybe_side_effect() } && true); - //~^ needless_if + //~^ needless_ifs // Don't leave trailing attributes #[allow(unused)] true; - //~^ needless_if + //~^ needless_ifs let () = if maybe_side_effect() {}; } + +fn issue15960() -> i32 { + matches!(2, 3); + //~^ needless_ifs + matches!(2, 3) == (2 * 2 == 5); + //~^ needless_ifs + + 1 // put something here so that `if` is a statement not an expression +} diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_ifs.rs similarity index 82% rename from src/tools/clippy/tests/ui/needless_if.rs rename to src/tools/clippy/tests/ui/needless_ifs.rs index 11103af5c5598..fb0ee5c9cc832 100644 --- a/src/tools/clippy/tests/ui/needless_if.rs +++ b/src/tools/clippy/tests/ui/needless_ifs.rs @@ -12,7 +12,7 @@ clippy::redundant_pattern_matching, unused )] -#![warn(clippy::needless_if)] +#![warn(clippy::needless_ifs)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -24,16 +24,16 @@ fn maybe_side_effect() -> bool { fn main() { // Lint if (true) {} - //~^ needless_if + //~^ needless_ifs // Do not remove the condition if maybe_side_effect() {} - //~^ needless_if + //~^ needless_ifs // Do not lint if (true) { } else { } if { - //~^ needless_if + //~^ needless_ifs return; } {} // Do not lint if `else if` is present @@ -48,7 +48,7 @@ fn main() { if true && let true = true {} // Can lint nested `if let`s if { - //~^ needless_if + //~^ needless_ifs if let true = true && true { @@ -93,15 +93,24 @@ fn main() { // Must be placed into an expression context to not be interpreted as a block if { maybe_side_effect() } {} - //~^ needless_if + //~^ needless_ifs // Would be a block followed by `&&true` - a double reference to `true` if { maybe_side_effect() } && true {} - //~^ needless_if + //~^ needless_ifs // Don't leave trailing attributes #[allow(unused)] if true {} - //~^ needless_if + //~^ needless_ifs let () = if maybe_side_effect() {}; } + +fn issue15960() -> i32 { + if matches!(2, 3) {} + //~^ needless_ifs + if matches!(2, 3) == (2 * 2 == 5) {} + //~^ needless_ifs + + 1 // put something here so that `if` is a statement not an expression +} diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_ifs.stderr similarity index 64% rename from src/tools/clippy/tests/ui/needless_if.stderr rename to src/tools/clippy/tests/ui/needless_ifs.stderr index 4b56843bd5229..8684ec217a0a4 100644 --- a/src/tools/clippy/tests/ui/needless_if.stderr +++ b/src/tools/clippy/tests/ui/needless_ifs.stderr @@ -1,20 +1,20 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:26:5 + --> tests/ui/needless_ifs.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it | - = note: `-D clippy::needless-if` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` + = note: `-D clippy::needless-ifs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_ifs)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:29:5 + --> tests/ui/needless_ifs.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_if.rs:35:5 + --> tests/ui/needless_ifs.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:50:5 + --> tests/ui/needless_ifs.rs:50:5 | LL | / if { LL | | @@ -57,22 +57,34 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:95:5 + --> tests/ui/needless_ifs.rs:95:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:98:5 + --> tests/ui/needless_ifs.rs:98:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:103:5 + --> tests/ui/needless_ifs.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` -error: aborting due to 7 previous errors +error: this `if` branch is empty + --> tests/ui/needless_ifs.rs:110:5 + | +LL | if matches!(2, 3) {} + | ^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3);` + +error: this `if` branch is empty + --> tests/ui/needless_ifs.rs:112:5 + | +LL | if matches!(2, 3) == (2 * 2 == 5) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3) == (2 * 2 == 5);` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index 01db64a446c0f..9769ee0c3a37d 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -1,12 +1,9 @@ #![feature(try_blocks)] -#![allow( - clippy::eq_op, - clippy::single_match, - unused_assignments, - unused_variables, - clippy::while_immutable_condition -)] +#![expect(clippy::eq_op, clippy::single_match, clippy::while_immutable_condition)] //@no-rustfix + +use std::arch::asm; + fn test1() { let mut x = 0; loop { @@ -522,3 +519,30 @@ fn issue15350() { } } } + +fn issue15673() { + loop { + unsafe { + // No lint since we don't analyze the inside of the asm + asm! { + "/* {} */", + label { + break; + } + } + } + } + + //~v never_loop + loop { + unsafe { + asm! { + "/* {} */", + label { + break; + } + } + } + return; + } +} diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr index 4fda06cff4ab9..49392d971ee32 100644 --- a/src/tools/clippy/tests/ui/never_loop.stderr +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -1,5 +1,5 @@ error: this loop never actually loops - --> tests/ui/never_loop.rs:12:5 + --> tests/ui/never_loop.rs:9:5 | LL | / loop { ... | @@ -10,7 +10,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> tests/ui/never_loop.rs:36:5 + --> tests/ui/never_loop.rs:33:5 | LL | / loop { ... | @@ -19,7 +19,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:58:5 + --> tests/ui/never_loop.rs:55:5 | LL | / loop { ... | @@ -28,7 +28,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:62:9 + --> tests/ui/never_loop.rs:59:9 | LL | / while i == 0 { ... | @@ -36,7 +36,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:76:9 + --> tests/ui/never_loop.rs:73:9 | LL | / loop { ... | @@ -45,7 +45,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:114:5 + --> tests/ui/never_loop.rs:111:5 | LL | / while let Some(y) = x { ... | @@ -53,7 +53,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:123:5 + --> tests/ui/never_loop.rs:120:5 | LL | / for x in 0..10 { ... | @@ -67,7 +67,7 @@ LL + if let Some(x) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:173:5 + --> tests/ui/never_loop.rs:170:5 | LL | / 'outer: while a { ... | @@ -76,7 +76,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:190:9 + --> tests/ui/never_loop.rs:187:9 | LL | / while false { LL | | @@ -86,7 +86,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:243:13 + --> tests/ui/never_loop.rs:240:13 | LL | let _ = loop { | _____________^ @@ -99,7 +99,7 @@ LL | | }; | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:266:5 + --> tests/ui/never_loop.rs:263:5 | LL | / 'a: loop { LL | | @@ -110,7 +110,7 @@ LL | | } | |_____^ error: sub-expression diverges - --> tests/ui/never_loop.rs:271:17 + --> tests/ui/never_loop.rs:268:17 | LL | break 'a; | ^^^^^^^^ @@ -119,7 +119,7 @@ LL | break 'a; = help: to override `-D warnings` add `#[allow(clippy::diverging_sub_expression)]` error: this loop never actually loops - --> tests/ui/never_loop.rs:303:13 + --> tests/ui/never_loop.rs:300:13 | LL | / for _ in 0..20 { LL | | @@ -135,7 +135,7 @@ LL + if let Some(_) = (0..20).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:388:13 + --> tests/ui/never_loop.rs:385:13 | LL | / 'c: loop { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:400:5 + --> tests/ui/never_loop.rs:397:5 | LL | / loop { LL | | @@ -155,7 +155,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:405:5 + --> tests/ui/never_loop.rs:402:5 | LL | / loop { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:426:5 + --> tests/ui/never_loop.rs:423:5 | LL | / for v in 0..10 { LL | | @@ -183,7 +183,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:434:5 + --> tests/ui/never_loop.rs:431:5 | LL | / 'outer: for v in 0..10 { LL | | @@ -194,7 +194,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -202,7 +202,7 @@ LL | | break 'outer; LL | | } | |_________^ help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:440:9 + --> tests/ui/never_loop.rs:437:9 | LL | return; | ^^^^^^^ @@ -213,7 +213,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -222,7 +222,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:443:5 + --> tests/ui/never_loop.rs:440:5 | LL | / for v in 0..10 { LL | | @@ -239,7 +239,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:445:9 + --> tests/ui/never_loop.rs:442:9 | LL | / 'inner: loop { LL | | @@ -248,7 +248,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:471:5 + --> tests/ui/never_loop.rs:468:5 | LL | / 'a: for _ in 0..1 { LL | | @@ -264,7 +264,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:477:5 + --> tests/ui/never_loop.rs:474:5 | LL | / 'a: for i in 0..1 { LL | | @@ -288,7 +288,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:492:5 + --> tests/ui/never_loop.rs:489:5 | LL | / for v in 0..10 { LL | | @@ -311,7 +311,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:503:5 + --> tests/ui/never_loop.rs:500:5 | LL | / 'bar: for _ in 0..100 { LL | | @@ -321,7 +321,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -336,7 +336,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -346,7 +346,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:512:5 + --> tests/ui/never_loop.rs:509:5 | LL | / 'foo: for _ in 0..100 { LL | | @@ -356,7 +356,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -372,7 +372,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -383,7 +383,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:517:13 + --> tests/ui/never_loop.rs:514:13 | LL | / loop { LL | | @@ -392,5 +392,17 @@ LL | | break 'foo; LL | | } | |_____________^ -error: aborting due to 29 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:537:5 + | +LL | / loop { +LL | | unsafe { +LL | | asm! { +LL | | "/* {} */", +... | +LL | | return; +LL | | } + | |_____^ + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index f03f74dfafe21..d040ba6ee83cb 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -2,7 +2,7 @@ #![allow( unused, clippy::diverging_sub_expression, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_pattern_matching )] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed index c2377491f25b1..f50af147c60cf 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed @@ -1,4 +1,4 @@ -#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] +#![allow(unused, clippy::diverging_sub_expression, clippy::needless_ifs)] #![warn(clippy::nonminimal_bool)] fn methods_with_negation() { diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs index 1ae0f0064c6b6..0ecd4775035bf 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] +#![allow(unused, clippy::diverging_sub_expression, clippy::needless_ifs)] #![warn(clippy::nonminimal_bool)] fn methods_with_negation() { diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed index 4bf4b91888c8c..fe4a48989b0fb 100644 --- a/src/tools/clippy/tests/ui/op_ref.fixed +++ b/src/tools/clippy/tests/ui/op_ref.fixed @@ -111,7 +111,7 @@ mod issue_2597 { } } -#[allow(clippy::needless_if)] +#[allow(clippy::needless_ifs)] fn issue15063() { use std::ops::BitAnd; diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs index 9a192661aafcd..cd0d231497abc 100644 --- a/src/tools/clippy/tests/ui/op_ref.rs +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -111,7 +111,7 @@ mod issue_2597 { } } -#[allow(clippy::needless_if)] +#[allow(clippy::needless_ifs)] fn issue15063() { use std::ops::BitAnd; diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed index 340be7c7e932a..ddba1cd1291fa 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -1,6 +1,5 @@ #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] +#![expect(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} @@ -98,7 +97,7 @@ fn option_map_unit_fn() { if let Some(a) = option() { do_nothing(a) } //~^ option_map_unit_fn - if let Some(value) = option() { println!("{:?}", value) } + if let Some(value) = option() { println!("{value:?}") } //~^ option_map_unit_fn } diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs index d902c87379b77..7bd1fe92bdb6e 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -1,6 +1,5 @@ #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] +#![expect(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} @@ -98,7 +97,7 @@ fn option_map_unit_fn() { option().map(do_nothing); //~^ option_map_unit_fn - option().map(|value| println!("{:?}", value)); + option().map(|value| println!("{value:?}")); //~^ option_map_unit_fn } diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr index 2405aa9a7ccfa..70b6985406f44 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,173 +1,256 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:37:5 + --> tests/ui/option_map_unit_fn_fixable.rs:36:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Some(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:40:5 + --> tests/ui/option_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Some(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:43:5 + --> tests/ui/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); - | ^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { diverge(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(diverge); +LL + if let Some(x_field) = x.field { diverge(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:50:5 + --> tests/ui/option_map_unit_fn_fixable.rs:49:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| x.do_option_nothing(value + captured)); +LL + if let Some(value) = x.field { x.do_option_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:53:5 + --> tests/ui/option_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { x.do_option_plus_one(value + captured); }); +LL + if let Some(value) = x.field { x.do_option_plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:57:5 + --> tests/ui/option_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| do_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| do_nothing(value + captured)); +LL + if let Some(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:60:5 + --> tests/ui/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured) }); +LL + if let Some(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:63:5 + --> tests/ui/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured); }); +LL + if let Some(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:66:5 + --> tests/ui/option_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { do_nothing(value + captured); } }); +LL + if let Some(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:70:5 + --> tests/ui/option_map_unit_fn_fixable.rs:69:5 | LL | x.field.map(|value| diverge(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| diverge(value + captured)); +LL + if let Some(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:73:5 + --> tests/ui/option_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { diverge(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured) }); +LL + if let Some(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:76:5 + --> tests/ui/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { diverge(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured); }); +LL + if let Some(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:79:5 + --> tests/ui/option_map_unit_fn_fixable.rs:78:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { diverge(value + captured); } }); +LL + if let Some(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:85:5 + --> tests/ui/option_map_unit_fn_fixable.rs:84:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { let y = plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { let y = plus_one(value + captured); }); +LL + if let Some(value) = x.field { let y = plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:88:5 + --> tests/ui/option_map_unit_fn_fixable.rs:87:5 | LL | x.field.map(|value| { plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { plus_one(value + captured); }); +LL + if let Some(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:91:5 + --> tests/ui/option_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { plus_one(value + captured); } }); +LL + if let Some(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:95:5 + --> tests/ui/option_map_unit_fn_fixable.rs:94:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(ref value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|ref value| { do_nothing(value + captured) }); +LL + if let Some(ref value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:98:5 + --> tests/ui/option_map_unit_fn_fixable.rs:97:5 | LL | option().map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(a) = option() { do_nothing(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - option().map(do_nothing); +LL + if let Some(a) = option() { do_nothing(a) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:101:5 + --> tests/ui/option_map_unit_fn_fixable.rs:100:5 + | +LL | option().map(|value| println!("{value:?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - option().map(|value| println!("{value:?}")); +LL + if let Some(value) = option() { println!("{value:?}") } | -LL | option().map(|value| println!("{:?}", value)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = option() { println!("{:?}", value) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:108:5 + --> tests/ui/option_map_unit_fn_fixable.rs:107:5 | LL | x.map(|x| unsafe { f(x) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x) = x { unsafe { f(x) } }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.map(|x| unsafe { f(x) }); +LL + if let Some(x) = x { unsafe { f(x) } } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:110:5 + --> tests/ui/option_map_unit_fn_fixable.rs:109:5 | LL | x.map(|x| unsafe { { f(x) } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x) = x { unsafe { f(x) } }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.map(|x| unsafe { { f(x) } }); +LL + if let Some(x) = x { unsafe { f(x) } } + | error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs index dd2f80fefbeec..a6d7ea4a4c8d9 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs @@ -1,5 +1,6 @@ +//@no-rustfix #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] +#![allow(clippy::unnecessary_wraps, clippy::unnecessary_map_on_constructor)] // only fires before the fix fn do_nothing(_: T) {} @@ -11,14 +12,19 @@ fn plus_one(value: usize) -> usize { value + 1 } +struct HasOption { + field: Option, +} + #[rustfmt::skip] fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; x.field.map(|value| { do_nothing(value); do_nothing(value) }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn // Suggestion for the let block should be `{ ... }` as it's too difficult to build a // proper suggestion for these cases @@ -26,18 +32,22 @@ fn option_map_unit_fn() { do_nothing(value); do_nothing(value) }); - //~^^^^ ERROR: cannot find value + //~^^^^ option_map_unit_fn x.field.map(|value| { do_nothing(value); do_nothing(value); }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them Some(42).map(diverge); + //~^ option_map_unit_fn "12".parse::().ok().map(diverge); + //~^ option_map_unit_fn Some(plus_one(1)).map(do_nothing); + //~^ option_map_unit_fn // Should suggest `if let Some(_y) ...` to not override the existing foo variable let y = Some(42); y.map(do_nothing); + //~^ option_map_unit_fn } fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr index 8527850143295..8cc246a38d37b 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr @@ -1,27 +1,106 @@ -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:17:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value) }); +LL + if let Some(value) = x.field { ... } + | -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:20:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:26:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); +LL + if let Some(value) = x.field { ... } + | -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:25:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:31:5 + | +LL | / x.field.map(|value| { +LL | | do_nothing(value); +LL | | do_nothing(value) +LL | | }); + | |______^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { +LL - do_nothing(value); +LL - do_nothing(value) +LL - }); +LL + if let Some(value) = x.field { ... } | -LL | x.field.map(|value| { - | ^ not found in this scope -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:30:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:36:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value); }); +LL + if let Some(value) = x.field { ... } + | + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:40:5 + | +LL | Some(42).map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - Some(42).map(diverge); +LL + if let Some(a) = Some(42) { diverge(a) } + | + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:42:5 + | +LL | "12".parse::().ok().map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - "12".parse::().ok().map(diverge); +LL + if let Some(a) = "12".parse::().ok() { diverge(a) } + | + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:44:5 + | +LL | Some(plus_one(1)).map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - Some(plus_one(1)).map(do_nothing); +LL + if let Some(a) = Some(plus_one(1)) { do_nothing(a) } + | + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:49:5 + | +LL | y.map(do_nothing); + | ^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - y.map(do_nothing); +LL + if let Some(_y) = y { do_nothing(_y) } + | -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0425`. diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs index 42f03aae7bb82..136db902a9758 100644 --- a/src/tools/clippy/tests/ui/option_option.rs +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -1,52 +1,52 @@ -#![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wraps, clippy::manual_unwrap_or_default)] +#![warn(clippy::option_option)] +#![expect(clippy::unnecessary_wraps)] const C: Option> = None; -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option static S: Option> = None; -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option fn input(_: Option>) {} -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option fn output() -> Option> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option None } fn output_nested() -> Vec>> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option vec![None] } // The lint only generates one warning for this fn output_nested_nested() -> Option>> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option None } struct Struct { x: Option>, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } impl Struct { fn struct_fn() -> Option> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option None } } trait Trait { fn trait_fn() -> Option>; - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } enum Enum { Tuple(Option>), - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option Struct { x: Option> }, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } // The lint allows this @@ -88,7 +88,7 @@ mod issue_4298 { #[serde(default)] #[serde(borrow)] foo: Option>>, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom + //~^ option_option } #[allow(clippy::option_option)] diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr index 0cd048e400e40..1c39f9acae8e3 100644 --- a/src/tools/clippy/tests/ui/option_option.stderr +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -1,80 +1,100 @@ -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:4:10 | LL | const C: Option> = None; | ^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/option_option.rs:1:9 - | -LL | #![deny(clippy::option_option)] - | ^^^^^^^^^^^^^^^^^^^^^ + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases + = note: `-D clippy::option-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::option_option)]` -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:6:11 | LL | static S: Option> = None; | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:9:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:12:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:17:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:23:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option>`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:29:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:34:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:41:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:46:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:48:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:90:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option>`, or a custom enum if you need to distinguish all 3 cases error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/panicking_overflow_checks.rs b/src/tools/clippy/tests/ui/panicking_overflow_checks.rs index 29789c9497567..61dfca8b37286 100644 --- a/src/tools/clippy/tests/ui/panicking_overflow_checks.rs +++ b/src/tools/clippy/tests/ui/panicking_overflow_checks.rs @@ -1,5 +1,5 @@ #![warn(clippy::panicking_overflow_checks)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn test(a: u32, b: u32, c: u32) { if a + b < a {} diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.fixed b/src/tools/clippy/tests/ui/partialeq_to_none.fixed index e700cc56349f2..9288529423dd9 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.fixed +++ b/src/tools/clippy/tests/ui/partialeq_to_none.fixed @@ -1,5 +1,5 @@ #![warn(clippy::partialeq_to_none)] -#![allow(clippy::eq_op, clippy::needless_if)] +#![allow(clippy::eq_op, clippy::needless_ifs)] struct Foobar; diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.rs b/src/tools/clippy/tests/ui/partialeq_to_none.rs index 1bae076dd3377..7940251c18a2b 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.rs +++ b/src/tools/clippy/tests/ui/partialeq_to_none.rs @@ -1,5 +1,5 @@ #![warn(clippy::partialeq_to_none)] -#![allow(clippy::eq_op, clippy::needless_if)] +#![allow(clippy::eq_op, clippy::needless_ifs)] struct Foobar; diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index cbb78bff68bff..f0252c4484a5a 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -1,7 +1,12 @@ #![warn(clippy::precedence)] -#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] -#![allow(clippy::identity_op)] -#![allow(clippy::eq_op)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::clone_on_copy, + clippy::identity_op, + clippy::eq_op +)] macro_rules! trip { ($a:expr) => { @@ -35,3 +40,24 @@ fn main() { let b = 3; trip!(b * 8); } + +struct W(u8); +impl Clone for W { + fn clone(&self) -> Self { + W(1) + } +} + +fn closure_method_call() { + // Do not lint when the method call is applied to the block, both inside the closure + let f = |x: W| { x }.clone(); + assert!(matches!(f(W(0)), W(1))); + + let f = (|x: W| -> _ { x }).clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence + + let f = (move |x: W| -> _ { x }).clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence +} diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs index c73a4020e2e51..5d47462114b85 100644 --- a/src/tools/clippy/tests/ui/precedence.rs +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -1,7 +1,12 @@ #![warn(clippy::precedence)] -#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] -#![allow(clippy::identity_op)] -#![allow(clippy::eq_op)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::clone_on_copy, + clippy::identity_op, + clippy::eq_op +)] macro_rules! trip { ($a:expr) => { @@ -35,3 +40,24 @@ fn main() { let b = 3; trip!(b * 8); } + +struct W(u8); +impl Clone for W { + fn clone(&self) -> Self { + W(1) + } +} + +fn closure_method_call() { + // Do not lint when the method call is applied to the block, both inside the closure + let f = |x: W| { x }.clone(); + assert!(matches!(f(W(0)), W(1))); + + let f = |x: W| -> _ { x }.clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence + + let f = move |x: W| -> _ { x }.clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence +} diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 50cd8f4b81464..f086cfe028904 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -1,5 +1,5 @@ error: operator precedence might not be obvious - --> tests/ui/precedence.rs:16:5 + --> tests/ui/precedence.rs:21:5 | LL | 1 << 2 + 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)` @@ -8,40 +8,62 @@ LL | 1 << 2 + 3; = help: to override `-D warnings` add `#[allow(clippy::precedence)]` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:18:5 + --> tests/ui/precedence.rs:23:5 | LL | 1 + 2 << 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:20:5 + --> tests/ui/precedence.rs:25:5 | LL | 4 >> 1 + 1; | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:22:5 + --> tests/ui/precedence.rs:27:5 | LL | 1 + 3 >> 2; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:24:5 + --> tests/ui/precedence.rs:29:5 | LL | 1 ^ 1 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:26:5 + --> tests/ui/precedence.rs:31:5 | LL | 3 | 2 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:28:5 + --> tests/ui/precedence.rs:33:5 | LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: aborting due to 7 previous errors +error: precedence might not be obvious + --> tests/ui/precedence.rs:56:13 + | +LL | let f = |x: W| -> _ { x }.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider parenthesizing the closure + | +LL | let f = (|x: W| -> _ { x }).clone(); + | + + + +error: precedence might not be obvious + --> tests/ui/precedence.rs:60:13 + | +LL | let f = move |x: W| -> _ { x }.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider parenthesizing the closure + | +LL | let f = (move |x: W| -> _ { x }).clone(); + | + + + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/print.rs b/src/tools/clippy/tests/ui/print.rs deleted file mode 100644 index ee3d9dc0de037..0000000000000 --- a/src/tools/clippy/tests/ui/print.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![allow(clippy::print_literal, clippy::write_literal)] -#![warn(clippy::print_stdout, clippy::use_debug)] - -use std::fmt::{Debug, Display, Formatter, Result}; - -#[allow(dead_code)] -struct Foo; - -impl Display for Foo { - fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{:?}", 43.1415) - //~^ use_debug - } -} - -impl Debug for Foo { - fn fmt(&self, f: &mut Formatter) -> Result { - // ok, we can use `Debug` formatting in `Debug` implementations - write!(f, "{:?}", 42.718) - } -} - -fn main() { - println!("Hello"); - //~^ print_stdout - - print!("Hello"); - //~^ print_stdout - - print!("Hello {}", "World"); - //~^ print_stdout - - print!("Hello {:?}", "World"); - //~^ print_stdout - //~| use_debug - - print!("Hello {:#?}", "#orld"); - //~^ print_stdout - //~| use_debug - - assert_eq!(42, 1337); - - vec![1, 2]; -} diff --git a/src/tools/clippy/tests/ui/print.stderr b/src/tools/clippy/tests/ui/print.stderr deleted file mode 100644 index 9dd216bd1449d..0000000000000 --- a/src/tools/clippy/tests/ui/print.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error: use of `Debug`-based formatting - --> tests/ui/print.rs:11:20 - | -LL | write!(f, "{:?}", 43.1415) - | ^^^^ - | - = note: `-D clippy::use-debug` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` - -error: use of `println!` - --> tests/ui/print.rs:24:5 - | -LL | println!("Hello"); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::print-stdout` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` - -error: use of `print!` - --> tests/ui/print.rs:27:5 - | -LL | print!("Hello"); - | ^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:30:5 - | -LL | print!("Hello {}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:33:5 - | -LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:33:19 - | -LL | print!("Hello {:?}", "World"); - | ^^^^ - -error: use of `print!` - --> tests/ui/print.rs:37:5 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:37:19 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^ - -error: aborting due to 8 previous errors - diff --git a/src/tools/clippy/tests/ui/print_stdout.rs b/src/tools/clippy/tests/ui/print_stdout.rs new file mode 100644 index 0000000000000..a379457f18696 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_stdout.rs @@ -0,0 +1,23 @@ +#![expect(clippy::print_literal)] +#![warn(clippy::print_stdout)] + +fn main() { + println!("Hello"); + //~^ print_stdout + + print!("Hello"); + //~^ print_stdout + + print!("Hello {}", "World"); + //~^ print_stdout + + print!("Hello {:?}", "World"); + //~^ print_stdout + + print!("Hello {:#?}", "#orld"); + //~^ print_stdout + + assert_eq!(42, 1337); + + vec![1, 2]; +} diff --git a/src/tools/clippy/tests/ui/print_stdout.stderr b/src/tools/clippy/tests/ui/print_stdout.stderr new file mode 100644 index 0000000000000..e1360e59b4126 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_stdout.stderr @@ -0,0 +1,35 @@ +error: use of `println!` + --> tests/ui/print_stdout.rs:5:5 + | +LL | println!("Hello"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` + +error: use of `print!` + --> tests/ui/print_stdout.rs:8:5 + | +LL | print!("Hello"); + | ^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:11:5 + | +LL | print!("Hello {}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:14:5 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:17:5 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed index 1141b5db3ebf4..490948442e11f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -3,7 +3,7 @@ #![allow( clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] use std::task::Poll::{Pending, Ready}; diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs index f60ddf4683096..d40fe84693b00 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs @@ -3,7 +3,7 @@ #![allow( clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] use std::task::Poll::{Pending, Ready}; diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.fixed index 980455da2ee7e..b746bfd1c3584 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.fixed @@ -1,5 +1,5 @@ #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::needless_if, clippy::no_effect, clippy::nonminimal_bool)] +#![allow(clippy::needless_ifs, clippy::no_effect, clippy::nonminimal_bool)] macro_rules! condition { () => { diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.rs index 9cb0fe2e65f63..7be9f31e56bd6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_if_let_true.rs @@ -1,5 +1,5 @@ #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::needless_if, clippy::no_effect, clippy::nonminimal_bool)] +#![allow(clippy::needless_ifs, clippy::no_effect, clippy::nonminimal_bool)] macro_rules! condition { () => { diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed index 1cec19ab8c99d..7c05eca1b70e5 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -2,7 +2,7 @@ #![allow( clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args )] diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs index 123573a8602b6..1d4abca492813 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -2,7 +2,7 @@ #![allow( clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args )] diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index dc9d6491691f3..a54a4ce13d53f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -2,7 +2,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index 2e9714ad8e75f..7252fce8cce6d 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -2,7 +2,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed index 800889b5fda0a..735c444bb45b5 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed @@ -1,7 +1,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs index 1668c2ff2bbac..eeca51e8be760 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs @@ -1,7 +1,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index dab816716d598..261d82fc35c87 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -4,7 +4,7 @@ clippy::if_same_then_else, clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args, clippy::unnecessary_wraps )] diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 3fd70515d0847..6cae4cc4b6b07 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -4,7 +4,7 @@ clippy::if_same_then_else, clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args, clippy::unnecessary_wraps )] diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index fdd851414746b..c08819b21ff01 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -19,6 +19,7 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(clippy::empty_enums)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] @@ -42,6 +43,7 @@ #![allow(clippy::overly_complex_bool_expr)] #![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] +#![allow(clippy::needless_ifs)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] @@ -83,6 +85,7 @@ #![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` #![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::empty_enums)] //~ ERROR: lint `clippy::empty_enum` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` @@ -109,6 +112,7 @@ #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::needless_ifs)] //~ ERROR: lint `clippy::needless_if` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 591c8ca53ac22..0f6b58e144d95 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -19,6 +19,7 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(clippy::empty_enums)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] @@ -42,6 +43,7 @@ #![allow(clippy::overly_complex_bool_expr)] #![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] +#![allow(clippy::needless_ifs)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] @@ -83,6 +85,7 @@ #![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` #![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::empty_enum)] //~ ERROR: lint `clippy::empty_enum` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` @@ -109,6 +112,7 @@ #![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` #![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::needless_if)] //~ ERROR: lint `clippy::needless_if` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index b54fec8c57949..4a7799be37d71 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,442 +8,454 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::empty_enum` has been renamed to `clippy::empty_enums` + --> tests/ui/rename.rs:88:9 + | +LL | #![warn(clippy::empty_enum)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::empty_enums` + error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +error: lint `clippy::needless_if` has been renamed to `clippy::needless_ifs` + --> tests/ui/rename.rs:115:9 + | +LL | #![warn(clippy::needless_if)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_ifs` + error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:137:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` - --> tests/ui/rename.rs:135:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unchecked_duration_subtraction)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:136:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:142:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:143:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:140:9 + --> tests/ui/rename.rs:144:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:141:9 + --> tests/ui/rename.rs:145:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 74 previous errors +error: aborting due to 76 previous errors diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed index 6bee013c3f470..b810e85a45fac 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed @@ -1,6 +1,4 @@ #![warn(clippy::result_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} @@ -92,7 +90,7 @@ fn result_map_unit_fn() { if let Ok(ref value) = x.field { do_nothing(value + captured) } //~^ result_map_unit_fn - if let Ok(value) = x.field { println!("{:?}", value) } + if let Ok(value) = x.field { println!("{value:?}") } //~^ result_map_unit_fn } diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs index a206cfe6842ff..18cd557f04544 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs @@ -1,6 +1,4 @@ #![warn(clippy::result_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} @@ -92,7 +90,7 @@ fn result_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); //~^ result_map_unit_fn - x.field.map(|value| println!("{:?}", value)); + x.field.map(|value| println!("{value:?}")); //~^ result_map_unit_fn } diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr index eca844e06cc06..d8f6239764d13 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,149 +1,220 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:34:5 + --> tests/ui/result_map_unit_fn_fixable.rs:32:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Ok(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:37:5 + --> tests/ui/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Ok(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:40:5 + --> tests/ui/result_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(diverge); - | ^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { diverge(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(diverge); +LL + if let Ok(x_field) = x.field { diverge(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:47:5 + --> tests/ui/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| x.do_result_nothing(value + captured)); +LL + if let Ok(value) = x.field { x.do_result_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:50:5 + --> tests/ui/result_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { x.do_result_plus_one(value + captured); }); +LL + if let Ok(value) = x.field { x.do_result_plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:54:5 + --> tests/ui/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| do_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| do_nothing(value + captured)); +LL + if let Ok(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:57:5 + --> tests/ui/result_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured) }); +LL + if let Ok(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:60:5 + --> tests/ui/result_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured); }); +LL + if let Ok(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:63:5 + --> tests/ui/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { do_nothing(value + captured); } }); +LL + if let Ok(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:67:5 + --> tests/ui/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| diverge(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| diverge(value + captured)); +LL + if let Ok(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:70:5 + --> tests/ui/result_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { diverge(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured) }); +LL + if let Ok(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:73:5 + --> tests/ui/result_map_unit_fn_fixable.rs:71:5 | LL | x.field.map(|value| { diverge(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured); }); +LL + if let Ok(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:76:5 + --> tests/ui/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { diverge(value + captured); } }); +LL + if let Ok(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:82:5 + --> tests/ui/result_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { let y = plus_one(value + captured); }); +LL + if let Ok(value) = x.field { let y = plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:85:5 + --> tests/ui/result_map_unit_fn_fixable.rs:83:5 | LL | x.field.map(|value| { plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { plus_one(value + captured); }); +LL + if let Ok(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:88:5 + --> tests/ui/result_map_unit_fn_fixable.rs:86:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { plus_one(value + captured); } }); +LL + if let Ok(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:92:5 + --> tests/ui/result_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|ref value| { do_nothing(value + captured) }); +LL + if let Ok(ref value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:95:5 + --> tests/ui/result_map_unit_fn_fixable.rs:93:5 + | +LL | x.field.map(|value| println!("{value:?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| println!("{value:?}")); +LL + if let Ok(value) = x.field { println!("{value:?}") } | -LL | x.field.map(|value| println!("{:?}", value)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { println!("{:?}", value) }` error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs index fe3d8ece39f4c..e3f5c7f996dad 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs @@ -1,7 +1,8 @@ +//@no-rustfix #![warn(clippy::result_map_unit_fn)] #![feature(never_type)] -#![allow(unused, clippy::unnecessary_map_on_constructor)] -//@no-rustfix +#![allow(clippy::unnecessary_map_on_constructor)] + struct HasResult { field: Result, } diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr index a6e38d808afa7..9f80ec1bbbd07 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,58 +1,86 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:23:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:24:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value) }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:28:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:34:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:35:5 + | +LL | / x.field.map(|value| { +LL | | +LL | | +LL | | do_nothing(value); +LL | | do_nothing(value) +LL | | }); + | |______^ | -LL | // x.field.map(|value| { -LL | || -LL | || -LL | || do_nothing(value); -LL | || do_nothing(value) -LL | || }); - | ||______^- help: try: `if let Ok(value) = x.field { ... }` - | |______| +help: use `if let` instead + | +LL - x.field.map(|value| { +LL - +LL - +LL - do_nothing(value); +LL - do_nothing(value) +LL - }); +LL + if let Ok(value) = x.field { ... } | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:40:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:41:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value); }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:46:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:47:5 | LL | "12".parse::().map(diverge); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(a) = "12".parse::() { diverge(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - "12".parse::().map(diverge); +LL + if let Ok(a) = "12".parse::() { diverge(a) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:54:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:55:5 | LL | y.map(do_nothing); - | ^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(_y) = y { do_nothing(_y) }` + | ^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - y.map(do_nothing); +LL + if let Ok(_y) = y { do_nothing(_y) } + | error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs index 802d27449abf6..0a1e0b07d1eb6 100644 --- a/src/tools/clippy/tests/ui/search_is_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some.rs @@ -8,31 +8,6 @@ use option_helpers::IteratorFalsePositives; //@no-rustfix #[rustfmt::skip] fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - - // Check `find().is_some()`, multi-line case. - let _ = v.iter().find(|&x| { - //~^ search_is_some - *x < 0 - } - ).is_some(); - - // Check `position().is_some()`, multi-line case. - let _ = v.iter().position(|&x| { - //~^ search_is_some - x < 0 - } - ).is_some(); - - // Check `rposition().is_some()`, multi-line case. - let _ = v.iter().rposition(|&x| { - //~^ search_is_some - x < 0 - } - ).is_some(); - // Check that we don't lint if the caller is not an `Iterator` or string let falsepos = IteratorFalsePositives { foo: 0 }; let _ = falsepos.find().is_some(); @@ -49,31 +24,6 @@ fn main() { #[rustfmt::skip] fn is_none() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - - // Check `find().is_none()`, multi-line case. - let _ = v.iter().find(|&x| { - //~^ search_is_some - *x < 0 - } - ).is_none(); - - // Check `position().is_none()`, multi-line case. - let _ = v.iter().position(|&x| { - //~^ search_is_some - x < 0 - } - ).is_none(); - - // Check `rposition().is_none()`, multi-line case. - let _ = v.iter().rposition(|&x| { - //~^ search_is_some - x < 0 - } - ).is_none(); - // Check that we don't lint if the caller is not an `Iterator` or string let falsepos = IteratorFalsePositives { foo: 0 }; let _ = falsepos.find().is_none(); @@ -87,18 +37,3 @@ fn is_none() { let _ = (0..1).find(some_closure).is_none(); //~^ search_is_some } - -#[allow(clippy::match_like_matches_macro)] -fn issue15102() { - let values = [None, Some(3)]; - let has_even = values - //~^ search_is_some - .iter() - .find(|v| match v { - Some(x) if x % 2 == 0 => true, - _ => false, - }) - .is_some(); - - println!("{has_even}"); -} diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr index d5412f9011107..ccbab5320ee24 100644 --- a/src/tools/clippy/tests/ui/search_is_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some.stderr @@ -1,109 +1,17 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:16:13 - | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - = note: `-D clippy::search-is-some` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` - -error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:23:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:30:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:46:20 + --> tests/ui/search_is_some.rs:21:20 | LL | let _ = (0..1).find(some_closure).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` - -error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:57:13 | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation - -error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:64:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation - -error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:71:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:87:13 + --> tests/ui/search_is_some.rs:37:13 | LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:94:20 - | -LL | let has_even = values - | ____________________^ -LL | | -LL | | .iter() -LL | | .find(|v| match v { -... | -LL | | }) -LL | | .is_some(); - | |__________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: aborting due to 9 previous errors +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed index cc4dbc919d81d..720ed385fb480 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed @@ -24,14 +24,32 @@ fn main() { let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0); //~^ search_is_some let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1); + // Check `find().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|x| { + *x < 0 // + }); // Check `position().is_none()`, single-line case. let _ = !v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `position().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|&x| { + x < 0 // + }); // Check `rposition().is_none()`, single-line case. let _ = !v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `rposition().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|&x| { + x < 0 // + }); let s1 = String::from("hello world"); let s2 = String::from("world"); diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs index fa31a9ddedc66..1cb5f9c399590 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs @@ -27,14 +27,38 @@ fn main() { //~^ search_is_some .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) .is_none(); + // Check `find().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .find(|&x| { + *x < 0 // + }) + .is_none(); // Check `position().is_none()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_none(); //~^ search_is_some + // Check `position().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .position(|&x| { + x < 0 // + }) + .is_none(); // Check `rposition().is_none()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_none(); //~^ search_is_some + // Check `rposition().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .rposition(|&x| { + x < 0 // + }) + .is_none(); let s1 = String::from("hello world"); let s2 = String::from("world"); diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr index b079cf7ea361b..909248357169f 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr @@ -59,92 +59,158 @@ LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains( LL | | .is_none(); | |__________________^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none.rs:31:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .find(|&x| { +LL | | *x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|x| { +LL + *x < 0 // +LL ~ }); + | + error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some_fixable_none.rs:32:13 + --> tests/ui/search_is_some_fixable_none.rs:40:13 | LL | let _ = v.iter().position(|&x| x < 0).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` +error: called `is_none()` after searching an `Iterator` with `position` + --> tests/ui/search_is_some_fixable_none.rs:43:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .position(|&x| { +LL | | x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|&x| { +LL + x < 0 // +LL ~ }); + | + error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some_fixable_none.rs:36:13 + --> tests/ui/search_is_some_fixable_none.rs:52:13 | LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` +error: called `is_none()` after searching an `Iterator` with `rposition` + --> tests/ui/search_is_some_fixable_none.rs:55:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .rposition(|&x| { +LL | | x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|&x| { +LL + x < 0 // +LL ~ }); + | + error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:43:13 + --> tests/ui/search_is_some_fixable_none.rs:67:13 | LL | let _ = "hello world".find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:45:13 + --> tests/ui/search_is_some_fixable_none.rs:69:13 | LL | let _ = "hello world".find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:47:13 + --> tests/ui/search_is_some_fixable_none.rs:71:13 | LL | let _ = "hello world".find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:50:13 + --> tests/ui/search_is_some_fixable_none.rs:74:13 | LL | let _ = s1.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:52:13 + --> tests/ui/search_is_some_fixable_none.rs:76:13 | LL | let _ = s1.find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:54:13 + --> tests/ui/search_is_some_fixable_none.rs:78:13 | LL | let _ = s1.find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:57:13 + --> tests/ui/search_is_some_fixable_none.rs:81:13 | LL | let _ = s1[2..].find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:59:13 + --> tests/ui/search_is_some_fixable_none.rs:83:13 | LL | let _ = s1[2..].find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:61:13 + --> tests/ui/search_is_some_fixable_none.rs:85:13 | LL | let _ = s1[2..].find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2[2..])` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:78:25 + --> tests/ui/search_is_some_fixable_none.rs:102:25 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == &cc)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:95:30 + --> tests/ui/search_is_some_fixable_none.rs:119:30 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == cc)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:107:17 + --> tests/ui/search_is_some_fixable_none.rs:131:17 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:111:17 + --> tests/ui/search_is_some_fixable_none.rs:135:17 | LL | let _ = vfoo | _________________^ @@ -162,55 +228,55 @@ LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2); | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:120:17 + --> tests/ui/search_is_some_fixable_none.rs:144:17 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|a| a[0] == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:127:17 + --> tests/ui/search_is_some_fixable_none.rs:151:17 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:146:17 + --> tests/ui/search_is_some_fixable_none.rs:170:17 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:148:17 + --> tests/ui/search_is_some_fixable_none.rs:172:17 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:152:17 + --> tests/ui/search_is_some_fixable_none.rs:176:17 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:154:17 + --> tests/ui/search_is_some_fixable_none.rs:178:17 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:158:17 + --> tests/ui/search_is_some_fixable_none.rs:182:17 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:161:17 + --> tests/ui/search_is_some_fixable_none.rs:185:17 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:182:17 + --> tests/ui/search_is_some_fixable_none.rs:206:17 | LL | let _ = vfoo | _________________^ @@ -228,25 +294,25 @@ LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:199:17 + --> tests/ui/search_is_some_fixable_none.rs:223:17 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:205:17 + --> tests/ui/search_is_some_fixable_none.rs:229:17 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|x| (**x)[0] == 9)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:219:17 + --> tests/ui/search_is_some_fixable_none.rs:243:17 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:224:17 + --> tests/ui/search_is_some_fixable_none.rs:248:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] | _________________^ @@ -264,106 +330,106 @@ LL ~ .iter().any(|&&(&x, ref y)| x == *y); | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:247:17 + --> tests/ui/search_is_some_fixable_none.rs:271:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:249:17 + --> tests/ui/search_is_some_fixable_none.rs:273:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:259:17 + --> tests/ui/search_is_some_fixable_none.rs:283:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:261:17 + --> tests/ui/search_is_some_fixable_none.rs:285:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:263:17 + --> tests/ui/search_is_some_fixable_none.rs:287:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:280:17 + --> tests/ui/search_is_some_fixable_none.rs:304:17 | LL | let _ = v.iter().find(|x| **x == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:282:17 + --> tests/ui/search_is_some_fixable_none.rs:306:17 | LL | Foo.bar(v.iter().find(|x| **x == 42).is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:288:9 + --> tests/ui/search_is_some_fixable_none.rs:312:9 | LL | v.iter().find(|x| **x == 42).is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:294:9 + --> tests/ui/search_is_some_fixable_none.rs:318:9 | LL | v.iter().find(|x| **x == 42).is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:300:17 + --> tests/ui/search_is_some_fixable_none.rs:324:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:302:17 + --> tests/ui/search_is_some_fixable_none.rs:326:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:305:17 + --> tests/ui/search_is_some_fixable_none.rs:329:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:307:17 + --> tests/ui/search_is_some_fixable_none.rs:331:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:313:17 + --> tests/ui/search_is_some_fixable_none.rs:337:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:316:17 + --> tests/ui/search_is_some_fixable_none.rs:340:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:322:17 + --> tests/ui/search_is_some_fixable_none.rs:346:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:325:17 + --> tests/ui/search_is_some_fixable_none.rs:349:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` -error: aborting due to 54 previous errors +error: aborting due to 57 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed index c7a4422f37378..1213fdcf61197 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed @@ -25,14 +25,35 @@ fn main() { //~^ search_is_some let _ = (1..3) .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1); + // Check `find().is_some()`, multi-line case. + let _ = v + .iter() + .any(|x| { + //~^ search_is_some + *x < 0 + }); // Check `position().is_some()`, single-line case. let _ = v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `position().is_some()`, multi-line case. + let _ = v + .iter() + .any(|&x| { + //~^ search_is_some + x < 0 + }); // Check `rposition().is_some()`, single-line case. let _ = v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `rposition().is_some()`, multi-line case. + let _ = v + .iter() + .any(|&x| { + //~^ search_is_some + x < 0 + }); let s1 = String::from("hello world"); let s2 = String::from("world"); @@ -290,9 +311,19 @@ mod issue9120 { } } +#[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0)); //~^ search_is_some println!("{has_even}"); + + let has_even = values + .iter() + .any(|v| match &v { + //~^ search_is_some + Some(x) if x % 2 == 0 => true, + _ => false, + }); + println!("{has_even}"); } diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs index d6b1c67c9718c..4294a39333f20 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs @@ -27,14 +27,38 @@ fn main() { .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) //~^ search_is_some .is_some(); + // Check `find().is_some()`, multi-line case. + let _ = v + .iter() + .find(|&x| { + //~^ search_is_some + *x < 0 + }) + .is_some(); // Check `position().is_some()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_some(); //~^ search_is_some + // Check `position().is_some()`, multi-line case. + let _ = v + .iter() + .position(|&x| { + //~^ search_is_some + x < 0 + }) + .is_some(); // Check `rposition().is_some()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_some(); //~^ search_is_some + // Check `rposition().is_some()`, multi-line case. + let _ = v + .iter() + .rposition(|&x| { + //~^ search_is_some + x < 0 + }) + .is_some(); let s1 = String::from("hello world"); let s2 = String::from("world"); @@ -298,9 +322,20 @@ mod issue9120 { } } +#[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); //~^ search_is_some println!("{has_even}"); + + let has_even = values + .iter() + .find(|v| match v { + //~^ search_is_some + Some(x) if x % 2 == 0 => true, + _ => false, + }) + .is_some(); + println!("{has_even}"); } diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr index 551a670d937f2..cee1eb08876ba 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr @@ -58,92 +58,149 @@ LL | | LL | | .is_some(); | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:33:10 + | +LL | .find(|&x| { + | __________^ +LL | | +LL | | *x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|x| { +LL + +LL + *x < 0 +LL ~ }); + | + error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some_fixable_some.rs:32:22 + --> tests/ui/search_is_some_fixable_some.rs:40:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` +error: called `is_some()` after searching an `Iterator` with `position` + --> tests/ui/search_is_some_fixable_some.rs:45:10 + | +LL | .position(|&x| { + | __________^ +LL | | +LL | | x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|&x| { +LL + +LL + x < 0 +LL ~ }); + | + error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some_fixable_some.rs:36:22 + --> tests/ui/search_is_some_fixable_some.rs:52:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` +error: called `is_some()` after searching an `Iterator` with `rposition` + --> tests/ui/search_is_some_fixable_some.rs:57:10 + | +LL | .rposition(|&x| { + | __________^ +LL | | +LL | | x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|&x| { +LL + +LL + x < 0 +LL ~ }); + | + error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:42:27 + --> tests/ui/search_is_some_fixable_some.rs:66:27 | LL | let _ = "hello world".find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:44:27 + --> tests/ui/search_is_some_fixable_some.rs:68:27 | LL | let _ = "hello world".find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:46:27 + --> tests/ui/search_is_some_fixable_some.rs:70:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:49:16 + --> tests/ui/search_is_some_fixable_some.rs:73:16 | LL | let _ = s1.find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:51:16 + --> tests/ui/search_is_some_fixable_some.rs:75:16 | LL | let _ = s1.find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:53:16 + --> tests/ui/search_is_some_fixable_some.rs:77:16 | LL | let _ = s1.find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:56:21 + --> tests/ui/search_is_some_fixable_some.rs:80:21 | LL | let _ = s1[2..].find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:58:21 + --> tests/ui/search_is_some_fixable_some.rs:82:21 | LL | let _ = s1[2..].find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:60:21 + --> tests/ui/search_is_some_fixable_some.rs:84:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:77:44 + --> tests/ui/search_is_some_fixable_some.rs:101:44 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:94:49 + --> tests/ui/search_is_some_fixable_some.rs:118:49 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:106:29 + --> tests/ui/search_is_some_fixable_some.rs:130:29 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:112:14 + --> tests/ui/search_is_some_fixable_some.rs:136:14 | LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) | ______________^ @@ -152,55 +209,55 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:119:29 + --> tests/ui/search_is_some_fixable_some.rs:143:29 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:126:29 + --> tests/ui/search_is_some_fixable_some.rs:150:29 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:145:30 + --> tests/ui/search_is_some_fixable_some.rs:169:30 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:147:50 + --> tests/ui/search_is_some_fixable_some.rs:171:50 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:151:26 + --> tests/ui/search_is_some_fixable_some.rs:175:26 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:153:26 + --> tests/ui/search_is_some_fixable_some.rs:177:26 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:157:26 + --> tests/ui/search_is_some_fixable_some.rs:181:26 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:160:26 + --> tests/ui/search_is_some_fixable_some.rs:184:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:183:14 + --> tests/ui/search_is_some_fixable_some.rs:207:14 | LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2) | ______________^ @@ -209,25 +266,25 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:198:29 + --> tests/ui/search_is_some_fixable_some.rs:222:29 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.inner[0].bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:204:29 + --> tests/ui/search_is_some_fixable_some.rs:228:29 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| (**x)[0] == 9)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:218:29 + --> tests/ui/search_is_some_fixable_some.rs:242:29 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:225:14 + --> tests/ui/search_is_some_fixable_some.rs:249:14 | LL | .find(|&&&(&x, ref y)| x == *y) | ______________^ @@ -236,64 +293,85 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:246:26 + --> tests/ui/search_is_some_fixable_some.rs:270:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:248:26 + --> tests/ui/search_is_some_fixable_some.rs:272:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:258:26 + --> tests/ui/search_is_some_fixable_some.rs:282:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:260:26 + --> tests/ui/search_is_some_fixable_some.rs:284:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:262:26 + --> tests/ui/search_is_some_fixable_some.rs:286:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:278:18 + --> tests/ui/search_is_some_fixable_some.rs:302:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:288:26 + --> tests/ui/search_is_some_fixable_some.rs:312:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:292:26 + --> tests/ui/search_is_some_fixable_some.rs:316:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:296:26 + --> tests/ui/search_is_some_fixable_some.rs:320:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:303:34 + --> tests/ui/search_is_some_fixable_some.rs:328:34 | LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))` -error: aborting due to 47 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:334:10 + | +LL | .find(|v| match v { + | __________^ +LL | | +LL | | Some(x) if x % 2 == 0 => true, +LL | | _ => false, +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|v| match &v { +LL + +LL + Some(x) if x % 2 == 0 => true, +LL + _ => false, +LL ~ }); + | + +error: aborting due to 51 previous errors diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 05009b2ddd416..d589bbc4affde 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -3,7 +3,7 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] #![allow( clippy::let_unit_value, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_locals )] diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index 03982b069e675..fe28674ec18e5 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -3,7 +3,7 @@ #![allow( unused, clippy::uninlined_format_args, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_pattern_matching, clippy::manual_unwrap_or_default diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index e28128e35adab..0f558cb5f786c 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -3,7 +3,7 @@ #![allow( unused, clippy::uninlined_format_args, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_pattern_matching, clippy::manual_unwrap_or_default diff --git a/src/tools/clippy/tests/ui/single_match_else_deref_patterns.fixed b/src/tools/clippy/tests/ui/single_match_else_deref_patterns.fixed index 7a9f806309645..1743ae6bf5a7d 100644 --- a/src/tools/clippy/tests/ui/single_match_else_deref_patterns.fixed +++ b/src/tools/clippy/tests/ui/single_match_else_deref_patterns.fixed @@ -5,7 +5,7 @@ clippy::op_ref, clippy::deref_addrof, clippy::borrow_deref_ref, - clippy::needless_if + clippy::needless_ifs )] #![deny(clippy::single_match_else)] diff --git a/src/tools/clippy/tests/ui/single_match_else_deref_patterns.rs b/src/tools/clippy/tests/ui/single_match_else_deref_patterns.rs index ef19c7cbde2ba..d5cb8a24a609c 100644 --- a/src/tools/clippy/tests/ui/single_match_else_deref_patterns.rs +++ b/src/tools/clippy/tests/ui/single_match_else_deref_patterns.rs @@ -5,7 +5,7 @@ clippy::op_ref, clippy::deref_addrof, clippy::borrow_deref_ref, - clippy::needless_if + clippy::needless_ifs )] #![deny(clippy::single_match_else)] diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed index b4f9d5867703a..f2fab9217604b 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.fixed +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] +#![allow(clippy::needless_ifs, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs index dae04ac4a62d5..1460d77a094dd 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.rs +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] +#![allow(clippy::needless_ifs, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs index 072e7b27b0d0a..8574cf1c20a1a 100644 --- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::let_unit_value, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs index ab9bdde6cf902..19f8b231925b7 100644 --- a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs +++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs @@ -1,5 +1,5 @@ #![warn(clippy::suspicious_unary_op_formatting)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[rustfmt::skip] fn main() { diff --git a/src/tools/clippy/tests/ui/test_attr_in_doctest.rs b/src/tools/clippy/tests/ui/test_attr_in_doctest.rs index 6cffd813b8358..7d1a09024895b 100644 --- a/src/tools/clippy/tests/ui/test_attr_in_doctest.rs +++ b/src/tools/clippy/tests/ui/test_attr_in_doctest.rs @@ -21,7 +21,6 @@ /// } /// /// #[test] -//~^ test_attr_in_doctest /// fn should_be_linted_too() { /// assert_eq!("#[test]", " /// #[test] diff --git a/src/tools/clippy/tests/ui/test_attr_in_doctest.stderr b/src/tools/clippy/tests/ui/test_attr_in_doctest.stderr index 166b9b417b626..a8fa53034036b 100644 --- a/src/tools/clippy/tests/ui/test_attr_in_doctest.stderr +++ b/src/tools/clippy/tests/ui/test_attr_in_doctest.stderr @@ -1,11 +1,8 @@ error: unit tests in doctest are not executed --> tests/ui/test_attr_in_doctest.rs:6:5 | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_be_linted() { - | |_______________________^ +LL | /// #[test] + | ^^^^^^^ | = note: `-D clippy::test-attr-in-doctest` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::test_attr_in_doctest)]` @@ -13,20 +10,11 @@ LL | | /// fn should_be_linted() { error: unit tests in doctest are not executed --> tests/ui/test_attr_in_doctest.rs:16:5 | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_also_be_linted() { - | |____________________________^ +LL | /// #[test] + | ^^^^^^^ +... +LL | /// #[test] + | ^^^^^^^ -error: unit tests in doctest are not executed - --> tests/ui/test_attr_in_doctest.rs:23:5 - | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_be_linted_too() { - | |___________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs index 0a0fe3a1990b0..839987a474cd0 100644 --- a/src/tools/clippy/tests/ui/unit_cmp.rs +++ b/src/tools/clippy/tests/ui/unit_cmp.rs @@ -3,7 +3,7 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #[derive(PartialEq)] diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr index a879633e10f2a..8c33c08c267d5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -1,17 +1,17 @@ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:4:13 + --> tests/ui/unnecessary_filter_map.rs:4:20 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:7:13 + --> tests/ui/unnecessary_filter_map.rs:7:20 | LL | let _ = (0..4).filter_map(|x| { - | _____________^ + | ____________________^ LL | | LL | | LL | | if x > 1 { @@ -21,10 +21,10 @@ LL | | }); | |______^ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:15:13 + --> tests/ui/unnecessary_filter_map.rs:15:20 | LL | let _ = (0..4).filter_map(|x| match x { - | _____________^ + | ____________________^ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), @@ -32,22 +32,22 @@ LL | | }); | |______^ error: this `.filter_map(..)` can be written more simply using `.map(..)` - --> tests/ui/unnecessary_filter_map.rs:21:13 + --> tests/ui/unnecessary_filter_map.rs:21:20 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `.filter_map(..)` is unnecessary - --> tests/ui/unnecessary_filter_map.rs:28:61 + --> tests/ui/unnecessary_filter_map.rs:28:46 | LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:166:14 + --> tests/ui/unnecessary_filter_map.rs:166:33 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs index 33ba7074d623b..c5a8937488d98 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - fn main() { let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); //~^ unnecessary_find_map diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr index 3754a3d99538e..8a269119df220 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr @@ -1,17 +1,17 @@ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:4:13 + --> tests/ui/unnecessary_find_map.rs:2:20 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:7:13 + --> tests/ui/unnecessary_find_map.rs:5:20 | LL | let _ = (0..4).find_map(|x| { - | _____________^ + | ____________________^ LL | | LL | | LL | | if x > 1 { @@ -21,10 +21,10 @@ LL | | }); | |______^ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:15:13 + --> tests/ui/unnecessary_find_map.rs:13:20 | LL | let _ = (0..4).find_map(|x| match x { - | _____________^ + | ____________________^ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), @@ -32,16 +32,16 @@ LL | | }); | |______^ error: this `.find_map(..)` can be written more simply using `.map(..).next()` - --> tests/ui/unnecessary_find_map.rs:21:13 + --> tests/ui/unnecessary_find_map.rs:19:20 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:33:14 + --> tests/ui/unnecessary_find_map.rs:31:33 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs index 4440089b3633b..d82a7b969080b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs +++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs @@ -1,5 +1,5 @@ #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(clippy::let_unit_value, clippy::missing_safety_doc, clippy::needless_if)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc, clippy::needless_ifs)] mod unsafe_items_invalid_comment { // SAFETY: diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed index 7e6a493b86c12..32494435fff5e 100644 --- a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(stmt_expr_attributes)] #![deny(clippy::unneeded_wildcard_pattern)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs index c91383e2cca5f..b3a0fb6098d61 100644 --- a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(stmt_expr_attributes)] #![deny(clippy::unneeded_wildcard_pattern)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 339d4a95084ae..0391fb19b1f6f 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -4,7 +4,7 @@ clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index f5c99183b0c51..f0702668fa917 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -4,7 +4,7 @@ clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed index 6d601ea5e57fe..2c794463457fb 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed @@ -3,7 +3,7 @@ #![allow( clippy::cognitive_complexity, clippy::match_ref_pats, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs index 7e5ea0161bffd..12f3d3fca464e 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs @@ -3,7 +3,7 @@ #![allow( clippy::cognitive_complexity, clippy::match_ref_pats, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] diff --git a/src/tools/clippy/tests/ui/use_debug.rs b/src/tools/clippy/tests/ui/use_debug.rs new file mode 100644 index 0000000000000..89c127a12faf9 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_debug.rs @@ -0,0 +1,50 @@ +#![warn(clippy::use_debug)] + +use std::fmt::{Debug, Display, Formatter, Result}; + +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", 43.1415) + //~^ use_debug + } +} + +impl Debug for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } +} + +fn main() { + print!("Hello {:?}", "World"); + //~^ use_debug + + print!("Hello {:#?}", "#orld"); + //~^ use_debug + + assert_eq!(42, 1337); + + vec![1, 2]; +} + +// don't get confused by nested impls +fn issue15942() { + struct Bar; + impl Debug for Bar { + fn fmt(&self, f: &mut Formatter) -> Result { + struct Baz; + impl Debug for Baz { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } + + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } +} diff --git a/src/tools/clippy/tests/ui/use_debug.stderr b/src/tools/clippy/tests/ui/use_debug.stderr new file mode 100644 index 0000000000000..85168d3bc3416 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_debug.stderr @@ -0,0 +1,23 @@ +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:9:20 + | +LL | write!(f, "{:?}", 43.1415) + | ^^^^ + | + = note: `-D clippy::use-debug` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:22:19 + | +LL | print!("Hello {:?}", "World"); + | ^^^^ + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:25:19 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 2942f64741e91..9de7d2c67149d 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] +#![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index f2da414e9f652..38cd1175aa485 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] +#![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs index d16506d94aa3c..157c236a214cd 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.rs +++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs @@ -1,6 +1,6 @@ #![deny(clippy::useless_conversion)] #![allow( - clippy::needless_if, + clippy::needless_ifs, clippy::unnecessary_fallible_conversions, clippy::manual_unwrap_or_default )] diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/while_immutable_condition.rs similarity index 98% rename from src/tools/clippy/tests/ui/infinite_loop.rs rename to src/tools/clippy/tests/ui/while_immutable_condition.rs index 8ff7f3b0c18d8..5c18cd41ff792 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.rs +++ b/src/tools/clippy/tests/ui/while_immutable_condition.rs @@ -1,3 +1,5 @@ +#![warn(clippy::while_immutable_condition)] + fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/while_immutable_condition.stderr similarity index 76% rename from src/tools/clippy/tests/ui/infinite_loop.stderr rename to src/tools/clippy/tests/ui/while_immutable_condition.stderr index 04da9776c3025..278b473d23cee 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.stderr +++ b/src/tools/clippy/tests/ui/while_immutable_condition.stderr @@ -1,14 +1,15 @@ error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:20:11 + --> tests/ui/while_immutable_condition.rs:22:11 | LL | while y < 10 { | ^^^^^^ | = note: this may lead to an infinite or to a never running loop - = note: `#[deny(clippy::while_immutable_condition)]` on by default + = note: `-D clippy::while-immutable-condition` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::while_immutable_condition)]` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:27:11 + --> tests/ui/while_immutable_condition.rs:29:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +17,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:36:11 + --> tests/ui/while_immutable_condition.rs:38:11 | LL | while !cond { | ^^^^^ @@ -24,7 +25,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:82:11 + --> tests/ui/while_immutable_condition.rs:84:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +33,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:89:11 + --> tests/ui/while_immutable_condition.rs:91:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +41,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:95:11 + --> tests/ui/while_immutable_condition.rs:97:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +49,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:112:11 + --> tests/ui/while_immutable_condition.rs:114:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +57,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:119:11 + --> tests/ui/while_immutable_condition.rs:121:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +65,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:187:15 + --> tests/ui/while_immutable_condition.rs:189:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +73,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:197:11 + --> tests/ui/while_immutable_condition.rs:199:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +83,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:206:11 + --> tests/ui/while_immutable_condition.rs:208:11 | LL | while y < 10 { | ^^^^^^ diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 2b6ee67c37dc7..b468b52aea5bb 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -1,23 +1,12 @@ "use strict"; window.searchState = { - timeout: null, inputElem: document.getElementById("search-input"), lastSearch: '', clearInput: () => { searchState.inputElem.value = ""; searchState.filterLints(); }, - clearInputTimeout: () => { - if (searchState.timeout !== null) { - clearTimeout(searchState.timeout); - searchState.timeout = null - } - }, - resetInputTimeout: () => { - searchState.clearInputTimeout(); - setTimeout(searchState.filterLints, 50); - }, filterLints: () => { function matchesSearch(lint, terms, searchStr) { // Search by id @@ -42,8 +31,6 @@ window.searchState = { return true; } - searchState.clearInputTimeout(); - let searchStr = searchState.inputElem.value.trim().toLowerCase(); if (searchStr.startsWith("clippy::")) { searchStr = searchStr.slice(8); @@ -79,7 +66,7 @@ function handleInputChanged(event) { if (event.target !== document.activeElement) { return; } - searchState.resetInputTimeout(); + searchState.filterLints(); } function handleShortcut(ev) { @@ -149,27 +136,25 @@ function lintAnchor(event) { expandLint(id); } +const clipboardTimeouts = new Map(); function copyToClipboard(event) { event.preventDefault(); event.stopPropagation(); const clipboard = event.target; - let resetClipboardTimeout = null; - const resetClipboardIcon = clipboard.innerHTML; - - function resetClipboard() { - resetClipboardTimeout = null; - clipboard.innerHTML = resetClipboardIcon; - } - navigator.clipboard.writeText("clippy::" + clipboard.parentElement.id.slice(5)); - clipboard.innerHTML = "✓"; - if (resetClipboardTimeout !== null) { - clearTimeout(resetClipboardTimeout); - } - resetClipboardTimeout = setTimeout(resetClipboard, 1000); + clipboard.textContent = "✓"; + + clearTimeout(clipboardTimeouts.get(clipboard)); + clipboardTimeouts.set( + clipboard, + setTimeout(() => { + clipboard.textContent = "📋"; + clipboardTimeouts.delete(clipboard); + }, 1000) + ); } function handleBlur(event, elementId) { @@ -487,14 +472,6 @@ function generateSettings() { setupDropdown("version-filter"); } -function generateSearch() { - searchState.inputElem.addEventListener("change", handleInputChanged); - searchState.inputElem.addEventListener("input", handleInputChanged); - searchState.inputElem.addEventListener("keydown", handleInputChanged); - searchState.inputElem.addEventListener("keyup", handleInputChanged); - searchState.inputElem.addEventListener("paste", handleInputChanged); -} - function scrollToLint(lintId) { const target = document.getElementById(lintId); if (!target) { @@ -540,6 +517,8 @@ function parseURLFilters() { } function addListeners() { + searchState.inputElem.addEventListener("input", handleInputChanged); + disableShortcutsButton.addEventListener("change", () => { disableShortcuts = disableShortcutsButton.checked; storeValue("disable-shortcuts", disableShortcuts); @@ -594,7 +573,6 @@ disableShortcutsButton.checked = disableShortcuts; addListeners(); highlightLazily(); generateSettings(); -generateSearch(); parseURLFilters(); scrollToLintByURL(); filters.filterLints(); diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index cd7ee6fb4feab..5788fef82d70e 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -464,7 +464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -807,9 +807,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linereader" @@ -822,9 +822,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1394,15 +1394,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags 2.9.2", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index b34c39c225dc8..1a5b2c29d20be 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -10,9 +10,7 @@ edition = "2021" [dependencies] clap = "4.0.32" env_logger = "0.11" -# FIXME: Remove this pin once this rustix issue is resolved -# https://github.com/bytecodealliance/rustix/issues/1496 -libc = "=0.2.174" +libc = "0.2" mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" } mdbook-i18n-helpers = "0.3.3" mdbook-spec = { path = "../../doc/reference/mdbook-spec" } diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index fa9a3e33914b6..03752c371ae11 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -134,7 +134,8 @@ impl Rewrite for Pat { let mut_prefix = format_mutability(mutability).trim(); let (ref_kw, mut_infix) = match by_ref { - ByRef::Yes(rmutbl) => ("ref", format_mutability(rmutbl).trim()), + // FIXME(pin_ergonomics): format the pinnedness + ByRef::Yes(_, rmutbl) => ("ref", format_mutability(rmutbl).trim()), ByRef::No => ("", ""), }; let id_str = rewrite_ident(context, ident); diff --git a/tests/rustdoc-js/import-filter.js b/tests/rustdoc-js/import-filter.js new file mode 100644 index 0000000000000..408d0e3326179 --- /dev/null +++ b/tests/rustdoc-js/import-filter.js @@ -0,0 +1,20 @@ +// This test ensures that when filtering on `import`, `externcrate` items are also displayed. +// It also ensures that the opposite is not true. + +const EXPECTED = [ + { + 'query': 'import:st', + 'others': [ + { 'path': 'foo', 'name': 'st', 'href': '../foo/index.html#reexport.st' }, + // FIXME: `href` is wrong: + { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + ], + }, + { + 'query': 'externcrate:st', + 'others': [ + // FIXME: `href` is wrong: + { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + ], + }, +]; diff --git a/tests/rustdoc-js/import-filter.rs b/tests/rustdoc-js/import-filter.rs new file mode 100644 index 0000000000000..7d30d00f5bbf8 --- /dev/null +++ b/tests/rustdoc-js/import-filter.rs @@ -0,0 +1,7 @@ +#![crate_name = "foo"] + +pub extern crate std as st2; + +pub use crate::Bar as st; + +pub struct Bar; diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 258f21324661b..d25e7f094964e 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -27,7 +27,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `amx-movrs` `amx-tf32` `amx-tile` -`amx-transpose` `apxf` `atomics` `avx` diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 7746654555dd8..07aba0d1d2815 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -2,11 +2,11 @@ use std::pin::Pin; +#[pin_v2] //~ ERROR the `#[pin_v2]` attribute is an experimental feature struct Foo; impl Foo { - fn foo(self: Pin<&mut Self>) { - } + fn foo(self: Pin<&mut Self>) {} fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } @@ -51,11 +51,11 @@ fn borrows() { mod not_compiled { use std::pin::Pin; + #[pin_v2] struct Foo; impl Foo { - fn foo(self: Pin<&mut Self>) { - } + fn foo(self: Pin<&mut Self>) {} fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index a8890254facea..cff564c01fc6b 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -148,6 +148,16 @@ LL | let x: Pin<&_> = &pin const Foo; = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: the `#[pin_v2]` attribute is an experimental feature + --> $DIR/feature-gate-pin_ergonomics.rs:5:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0382]: use of moved value: `x` --> $DIR/feature-gate-pin_ergonomics.rs:28:9 | @@ -177,16 +187,16 @@ LL | x.foo(); | ^ value used here after move | note: `Foo::foo` takes ownership of the receiver `self`, which moves `x` - --> $DIR/feature-gate-pin_ergonomics.rs:8:12 + --> $DIR/feature-gate-pin_ergonomics.rs:9:12 | -LL | fn foo(self: Pin<&mut Self>) { +LL | fn foo(self: Pin<&mut Self>) {} | ^^^^ help: consider reborrowing the `Pin` instead of moving it | LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors Some errors have detailed explanations: E0382, E0658. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr index e650eeca48ad9..72c066426bd99 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr @@ -1,6 +1,8 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/assoc-type.rs:11:19 | +LL | impl MyTrait for &i32 { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Output = &i32; | ^ this lifetime must come from the implemented type diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs new file mode 100644 index 0000000000000..5401bc4ecb873 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -0,0 +1,19 @@ +struct S; +struct T; + +impl<'a> IntoIterator for &S { + //~^ ERROR E0207 + //~| NOTE there is a named lifetime specified on the impl block you could use + //~| NOTE unconstrained lifetime parameter + //~| HELP consider using the named lifetime here instead of an implicit lifetime + type Item = &T; + //~^ ERROR in the trait associated type + //~| HELP consider using the lifetime from the impl block + //~| NOTE this lifetime must come from the implemented type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr new file mode 100644 index 0000000000000..feac49eb0ff52 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -0,0 +1,28 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-1.rs:9:17 + | +LL | impl<'a> IntoIterator for &S { + | ---- there is a named lifetime specified on the impl block you could use +... +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + | +help: consider using the lifetime from the impl block + | +LL | type Item = &'a T; + | ++ + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6 + | +LL | impl<'a> IntoIterator for &S { + | ^^ unconstrained lifetime parameter + | +help: consider using the named lifetime here instead of an implicit lifetime + | +LL | impl<'a> IntoIterator for &'a S { + | ++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs new file mode 100644 index 0000000000000..dd720f075ac46 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + //~^ ERROR use of undeclared lifetime name `'a` + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr new file mode 100644 index 0000000000000..7a0246eaac8fd --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr @@ -0,0 +1,22 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 + | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57 + | +LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | impl<'a> IntoIterator for &S { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs new file mode 100644 index 0000000000000..60d1f0f8fe571 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter = std::collections::btree_map::Values; + //~^ ERROR missing lifetime specifier + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr new file mode 100644 index 0000000000000..408d5bb40664d --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr @@ -0,0 +1,25 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-3.rs:5:17 + | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56 + | +LL | type IntoIter = std::collections::btree_map::Values; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL ~ impl<'a> IntoIterator for &S { +LL | type Item = &T; +LL | +LL ~ type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs new file mode 100644 index 0000000000000..0c99e8874c354 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + //~^ ERROR lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr new file mode 100644 index 0000000000000..ebe051509aad2 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr @@ -0,0 +1,17 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-4.rs:5:17 + | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0195]: lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration + --> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18 + | +LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + | ^^^^ lifetimes do not match associated type in trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs new file mode 100644 index 0000000000000..17cca7cc9e37b --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs @@ -0,0 +1,19 @@ +struct S; +struct T; + +impl<'a> IntoIterator for &'_ S { + //~^ ERROR E0207 + //~| NOTE there is a named lifetime specified on the impl block you could use + //~| NOTE unconstrained lifetime parameter + //~| HELP consider using the named lifetime here instead of an implicit lifetime + type Item = &T; + //~^ ERROR in the trait associated type + //~| HELP consider using the lifetime from the impl block + //~| NOTE this lifetime must come from the implemented type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr new file mode 100644 index 0000000000000..cb15bcb7d5049 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr @@ -0,0 +1,29 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-5.rs:9:17 + | +LL | impl<'a> IntoIterator for &'_ S { + | ---- there is a named lifetime specified on the impl block you could use +... +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + | +help: consider using the lifetime from the impl block + | +LL | type Item = &'a T; + | ++ + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/missing-lifetime-in-assoc-type-5.rs:4:6 + | +LL | impl<'a> IntoIterator for &'_ S { + | ^^ unconstrained lifetime parameter + | +help: consider using the named lifetime here instead of an implicit lifetime + | +LL - impl<'a> IntoIterator for &'_ S { +LL + impl<'a> IntoIterator for &'a S { + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr index 340ef93588515..cadba149c234d 100644 --- a/tests/ui/lifetimes/no_lending_iterators.stderr +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -13,6 +13,8 @@ LL | impl Iterator for Data { error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/no_lending_iterators.rs:18:17 | +LL | impl Bar for usize { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &usize; | ^ this lifetime must come from the implemented type diff --git a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 5020395eb6aea..dc21c2e3cf9bf 100644 --- a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -6,8 +6,9 @@ LL | type Item = IteratorChunk; | help: consider introducing a named lifetime parameter | -LL | type Item<'a> = IteratorChunk<'a, T, S>; - | ++++ +++ +LL ~ impl<'a, T, S: Iterator> Iterator for ChunkingIterator { +LL ~ type Item = IteratorChunk<'a, T, S>; + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr index 42551b87f6234..026f5b5f80a9b 100644 --- a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr +++ b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr @@ -17,10 +17,6 @@ LL | type T = &'missing (); | help: consider introducing lifetime `'missing` here | -LL | type T<'missing> = &'missing (); - | ++++++++++ -help: consider introducing lifetime `'missing` here - | LL | impl<'missing> Lt<'missing> for () { | ++++++++++ diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr new file mode 100644 index 0000000000000..96bac039df723 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr @@ -0,0 +1,155 @@ +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:48:28 + | +LL | let Foo { x, y } = foo.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:38:24 + | +LL | let Foo { x, y } = foo.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:70:28 + | +LL | let Foo { x, y } = foo.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:60:24 + | +LL | let Foo { x, y } = foo.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:92:25 + | +LL | let Bar(x, y) = bar.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:82:21 + | +LL | let Bar(x, y) = bar.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:114:25 + | +LL | let Bar(x, y) = bar.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:104:21 + | +LL | let Bar(x, y) = bar.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_ref(); + | +++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs new file mode 100644 index 0000000000000..3ec9b8fe476f1 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs @@ -0,0 +1,124 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +//@[pin_ergonomics] check-pass +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +// This test verifies that the `pin_ergonomics` feature works well +// together with the `deref_patterns` feature. + +use std::pin::Pin; + +#[cfg_attr(pin_ergonomics, pin_v2)] +struct Foo { + x: T, + y: U, +} + +#[cfg_attr(pin_ergonomics, pin_v2)] +struct Bar(T, U); + +#[cfg_attr(pin_ergonomics, pin_v2)] +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +trait IsPinMut {} +trait IsPinConst {} +impl IsPinMut for Pin<&mut T> {} +impl IsPinConst for Pin<&T> {} + +fn assert_pin_mut(_: T) {} +fn assert_pin_const(_: T) {} + +fn foo_mut(mut foo: Pin<&mut Foo>) { + let Foo { .. } = foo.as_mut(); + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = foo.as_mut(); + + let _ = || { + let Foo { .. } = foo.as_mut(); + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = foo.as_mut(); + }; +} + +fn foo_const(foo: Pin<&Foo>) { + let Foo { .. } = foo.as_ref(); + let Foo { x, y } = foo.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = foo.as_ref(); + + let _ = || { + let Foo { .. } = foo.as_ref(); + let Foo { x, y } = foo.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = foo.as_ref(); + }; +} + +fn bar_mut(mut bar: Pin<&mut Bar>) { + let Bar(..) = bar.as_mut(); + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = bar.as_mut(); + + let _ = || { + let Bar(..) = bar.as_mut(); + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = bar.as_mut(); + }; +} + +fn bar_const(bar: Pin<&Bar>) { + let Bar(..) = bar.as_ref(); + let Bar(x, y) = bar.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = bar.as_ref(); + + let _ = || { + let Bar(..) = bar.as_ref(); + let Bar(x, y) = bar.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = bar.as_ref(); + }; +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr new file mode 100644 index 0000000000000..9bcb41299b7da --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr @@ -0,0 +1,142 @@ +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr new file mode 100644 index 0000000000000..fa2e418591c1f --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr @@ -0,0 +1,92 @@ +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut NonPinProject>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut NonPinProject>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&NonPinProject>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&NonPinProject>` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr new file mode 100644 index 0000000000000..0e55a51702842 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr @@ -0,0 +1,243 @@ +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:33:9 + | +LL | let Foo { x, y } = foo.as_mut(); + | ^^^^^^^^^^^^ ------------ this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo.as_mut(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | match foo.as_mut() { + | ------------ this expression has type `Pin<&mut Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo.as_mut() { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 + | +LL | let _ = || match foo.as_mut() { + | ------------ this expression has type `Pin<&mut Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let _ = || match *foo.as_mut() { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:55:9 + | +LL | let Foo { x, y } = foo; + | ^^^^^^^^^^^^ --- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + | +LL | match foo { + | --- this expression has type `Pin<&Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 + | +LL | let _ = || match foo { + | --- this expression has type `Pin<&Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let _ = || match *foo { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:77:9 + | +LL | let Bar(x, y) = bar.as_mut(); + | ^^^^^^^^^ ------------ this expression has type `Pin<&mut Bar>` + | | + | expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar.as_mut(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | match bar.as_mut() { + | ------------ this expression has type `Pin<&mut Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar.as_mut() { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 + | +LL | let _ = || match bar.as_mut() { + | ------------ this expression has type `Pin<&mut Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let _ = || match *bar.as_mut() { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:99:9 + | +LL | let Bar(x, y) = bar; + | ^^^^^^^^^ --- this expression has type `Pin<&Bar>` + | | + | expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + | +LL | match bar { + | --- this expression has type `Pin<&Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 + | +LL | let _ = || match bar { + | --- this expression has type `Pin<&Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let _ = || match *bar { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&mut NonPinProject>` + | | + | expected `Pin<&mut NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&mut NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let NonPinProject { x } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&NonPinProject>` + | | + | expected `Pin<&NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let NonPinProject { x } = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | match foo { + | --- this expression has type `Pin<&mut NonPinProject>` +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&mut NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | match bar { + | --- this expression has type `Pin<&NonPinProject>` +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ expected `Pin<&NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar { + | + + +error: aborting due to 16 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr new file mode 100644 index 0000000000000..9bcb41299b7da --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr @@ -0,0 +1,142 @@ +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_v2]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NonPinProject { + | + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs new file mode 100644 index 0000000000000..2b2a4e61abfd1 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs @@ -0,0 +1,148 @@ +//@ revisions: normal pin_ergonomics deref_patterns both +//@ edition:2024 +#![cfg_attr(any(pin_ergonomics, both), feature(pin_ergonomics))] +#![cfg_attr(any(deref_patterns, both), feature(deref_patterns))] +#![allow(incomplete_features)] + +// This test verifies that the `pin_ergonomics` feature works well +// together with the `deref_patterns` feature under the error: +// "mix of deref patterns and normal constructors". + +use std::pin::Pin; + +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] +struct Foo { + x: T, + y: U, +} + +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] +struct Bar(T, U); + +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +struct NonPinProject { + x: T, +} + +fn foo_mut(mut foo: Pin<&mut Foo>) { + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR mismatched types + + match foo.as_mut() { + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match foo.as_mut() { + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn foo_const(foo: Pin<&Foo>) { + let Foo { x, y } = foo; + //[normal]~^ ERROR mismatched types + + match foo { + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match foo { + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn bar_mut(bar: Pin<&mut Bar>) { + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR mismatched types + + match bar.as_mut() { + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match bar.as_mut() { + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn bar_const(bar: Pin<&Bar>) { + let Bar(x, y) = bar; + //[normal]~^ ERROR mismatched types + + match bar { + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match bar { + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn non_pin_project(foo: Pin<&mut NonPinProject>, bar: Pin<&NonPinProject>) { + let NonPinProject { x } = foo; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + let NonPinProject { x } = bar; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + + match foo { + NonPinProject { x } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_v2]` + Pin { .. } => {} + } + match bar { + NonPinProject { x } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_v2]` + Pin { .. } => {} + } +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr new file mode 100644 index 0000000000000..8ec481fba9e0d --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -0,0 +1,242 @@ +error[E0658]: the `#[pin_v2]` attribute is an experimental feature + --> $DIR/pattern-matching.rs:12:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[pin_v2]` attribute is an experimental feature + --> $DIR/pattern-matching.rs:18:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:33:9 + | +LL | let Foo { x, y } = foo_mut; + | ^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo_mut; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:38:9 + | +LL | let Foo { x, y } = foo_const; + | ^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo_const; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:46:9 + | +LL | match bar_mut { + | ------- this expression has type `Pin<&mut Bar>` +LL | Bar::Foo(x, y) => { + | ^^^^^^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found enum `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar_mut { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:51:18 + | +LL | _ if let Bar::Bar { x, y } = bar_mut => { + | ^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Bar>` + | | + | expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found enum `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | _ if let Bar::Bar { x, y } = *bar_mut => { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:59:9 + | +LL | match bar_const { + | --------- this expression has type `Pin<&Bar>` +LL | Bar::Bar { x, y } => { + | ^^^^^^^^^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found enum `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar_const { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:64:18 + | +LL | _ if let Bar::Foo(x, y) = bar_const => { + | ^^^^^^^^^^^^^^ --------- this expression has type `Pin<&Bar>` + | | + | expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found enum `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | _ if let Bar::Foo(x, y) = *bar_const => { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:74:9 + | +LL | let (Foo { x, y },) = foo_mut; + | ^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (Foo,)>` + | | + | expected `Pin<&mut (Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&mut (Foo,)>` + found tuple `(_,)` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let (Foo { x, y },) = *foo_mut; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:78:9 + | +LL | let (Foo { x, y },) = foo_const; + | ^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(Foo,)>` + | | + | expected `Pin<&(Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&(Foo,)>` + found tuple `(_,)` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let (Foo { x, y },) = *foo_const; + | + + +error[E0529]: expected an array or slice, found `Pin<&mut [Foo; 1]>` + --> $DIR/pattern-matching.rs:85:9 + | +LL | let [Foo { x, y }] = foo_mut; + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo; 1]>` + +error[E0529]: expected an array or slice, found `Pin<&[Foo; 1]>` + --> $DIR/pattern-matching.rs:89:9 + | +LL | let [Foo { x, y }] = foo_const; + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo; 1]>` + +error[E0529]: expected an array or slice, found `Pin<&mut [Foo]>` + --> $DIR/pattern-matching.rs:96:12 + | +LL | if let [Foo { x, y }] = foo_mut { + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo]>` + +error[E0529]: expected an array or slice, found `Pin<&[Foo]>` + --> $DIR/pattern-matching.rs:101:12 + | +LL | if let [Foo { x, y }] = foo_const { + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo]>` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:109:5 + | +LL | (&mut x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:115:5 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:121:5 + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut Foo,)` + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:127:5 + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (Foo,)` + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:133:5 + | +LL | (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:139:5 + | +LL | (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found tuple `(_,)` + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0308, E0529, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr new file mode 100644 index 0000000000000..8cb032b73d859 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr @@ -0,0 +1,54 @@ +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:115:6 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^ --------------------------------- expected due to this + | | + | expected `Foo`, found `&mut _` + | + = note: expected struct `Foo` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-matching.rs:115:6 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - (&mut x,): Pin<&'a mut &'a mut (Foo,)>, +LL + (x,): Pin<&'a mut &'a mut (Foo,)>, + | + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:121:5 + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut Foo,)` + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:127:5 + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (Foo,)` + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>.pointer, + | ++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs new file mode 100644 index 0000000000000..96a4705bf9b3b --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -0,0 +1,144 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![feature(if_let_guard, negative_impls)] +#![allow(incomplete_features)] + +use std::pin::Pin; + +// This test verifies that a `&pin mut T` can be projected to a pinned +// reference field `&pin mut T.U` when `T` is marked with `#[pin_v2]`. + +#[pin_v2] //[normal]~ ERROR the `#[pin_v2]` attribute is an experimental feature +struct Foo { + x: T, + y: U, +} + +#[pin_v2] //[normal]~ ERROR the `#[pin_v2]` attribute is an experimental feature +enum Bar { + Foo(T, U), + Bar { x: T, y: U }, +} + +trait IsPinMut {} +trait IsPinConst {} +impl IsPinMut for Pin<&mut T> {} +impl IsPinConst for Pin<&T> {} + +fn assert_pin_mut(_: T) {} +fn assert_pin_const(_: T) {} + +fn foo(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { + let Foo { x, y } = foo_mut; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + + let Foo { x, y } = foo_const; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); +} + +fn bar(bar_mut: Pin<&mut Bar>, bar_const: Pin<&Bar>) { + match bar_mut { + Bar::Foo(x, y) => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + } + _ if let Bar::Bar { x, y } = bar_mut => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + } + _ => {} + } + match bar_const { + Bar::Bar { x, y } => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + _ if let Bar::Foo(x, y) = bar_const => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + _ => {} + } +} + +fn pin_mut_tuple(foo_mut: Pin<&mut (Foo,)>, foo_const: Pin<&(Foo,)>) { + let (Foo { x, y },) = foo_mut; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + let (Foo { x, y },) = foo_const; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); +} + +fn pin_mut_array(foo_mut: Pin<&mut [Foo; 1]>, foo_const: Pin<&[Foo; 1]>) { + let [Foo { x, y }] = foo_mut; + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [Foo; 1]>` + assert_pin_mut(x); + assert_pin_mut(y); + let [Foo { x, y }] = foo_const; + //[normal]~^ ERROR expected an array or slice, found `Pin<&[Foo; 1]>` + assert_pin_const(x); + assert_pin_const(y); +} + +fn pin_mut_slice(foo_mut: Pin<&mut [Foo]>, foo_const: Pin<&[Foo]>) { + if let [Foo { x, y }] = foo_mut { + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [Foo]>` + assert_pin_mut(x); + assert_pin_mut(y); + } + if let [Foo { x, y }] = foo_const { + //[normal]~^ ERROR expected an array or slice, found `Pin<&[Foo]>` + assert_pin_const(x); + assert_pin_const(y); + } +} + +fn tuple_ref_mut_pat_and_pin_mut_of_tuple_mut_ty<'a, T, U>( + (&mut x,): Pin<&'a mut (&'a mut Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn tuple_ref_mut_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + (&mut x,): Pin<&'a mut &'a mut (Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T, U>( + &mut (x,): Pin<&'a mut (&'a mut Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + &mut (x,): Pin<&'a mut &'a mut (Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T, U>( + (x,): Pin<&'a mut (&'a mut Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut &'a mut Foo> { + x // ok +} + +fn tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + (x,): Pin<&'a mut &'a mut (Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.rs b/tests/ui/pin-ergonomics/pin_v2-attr.rs new file mode 100644 index 0000000000000..cfafe4b0b8999 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-attr.rs @@ -0,0 +1,138 @@ +#![feature( + pin_ergonomics, + where_clause_attrs, + trait_alias, + extern_types, + associated_type_defaults, + fn_delegation, +)] +#![allow(incomplete_features)] +#![pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on crates + +// allowed + +#[pin_v2] +struct Struct {} + +#[pin_v2] +enum Enum {} + +#[pin_v2] +union Union { + field: (), +} + +// disallowed + +enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + //~^ ERROR `#[pin_v2]` attribute cannot be used on function params + //~| ERROR `#[pin_v2]` attribute cannot be used on function params + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on enum variants + UnitVariant, + TupleVariant(#[pin_v2] T), //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + field: U, + }, +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on traits +trait Trait { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on associated consts + const ASSOC_CONST: () = (); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on associated types + type AssocType = (); + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on required trait methods + fn method(); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on provided trait methods + fn method_with_body() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait aliases +trait TraitAlias = Trait; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on inherent impl blocks +impl Struct { + // FIXME: delegation macros are not tested yet (how to?) + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on delegations + reuse ::type_id; + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on inherent methods + fn method() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait impl blocks +impl Trait for Enum { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait methods in impl blocks + fn method() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on extern crates +extern crate alloc; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on use statements +use std::pin::Pin; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on statics +static STATIC: () = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on constants +const CONST: () = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on functions +fn f(#[pin_v2] param: Foo) +//~^ ERROR `#[pin_v2]` attribute cannot be used on function params +//~| ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +where + #[pin_v2] + //~^ ERROR `#[pin_v2]` attribute cannot be used on where predicates + T:, +{ + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on closures + || (); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on expressions + [(), (), ()]; + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on statements + let _: Foo<(), ()> = Foo::StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + field: (), + }; + match param { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on match arms + Foo::UnitVariant => {} + Foo::TupleVariant(..) => {} + Foo::StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on pattern fields + field, + } => {} + } +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on modules +mod m {} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign modules +extern "C" { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign types + type ForeignTy; + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign statics + static EXTERN_STATIC: (); + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign functions + fn extern_fn(); +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on type alias +type Type = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro defs +macro_rules! macro_def { + () => {}; +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro calls +macro_def!(); + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr new file mode 100644 index 0000000000000..81e086f5a7ef8 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -0,0 +1,312 @@ +error: `#[pin_v2]` attribute cannot be used on macro calls + --> $DIR/pin_v2-attr.rs:135:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/pin_v2-attr.rs:84:12 + | +LL | fn f(#[pin_v2] param: Foo) + | ^^^^^^^^^ + +error: `#[pin_v2]` attribute cannot be used on crates + --> $DIR/pin_v2-attr.rs:10:1 + | +LL | #![pin_v2] + | ^^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:27:10 + | +LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:27:23 + | +LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on enum variants + --> $DIR/pin_v2-attr.rs:30:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:32:18 + | +LL | TupleVariant(#[pin_v2] T), + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:34:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on traits + --> $DIR/pin_v2-attr.rs:39:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on associated consts + --> $DIR/pin_v2-attr.rs:41:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on associated types + --> $DIR/pin_v2-attr.rs:43:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on required trait methods + --> $DIR/pin_v2-attr.rs:46:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on provided trait methods + --> $DIR/pin_v2-attr.rs:48:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait aliases + --> $DIR/pin_v2-attr.rs:52:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on inherent impl blocks + --> $DIR/pin_v2-attr.rs:55:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on delegations + --> $DIR/pin_v2-attr.rs:58:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on inherent methods + --> $DIR/pin_v2-attr.rs:61:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait impl blocks + --> $DIR/pin_v2-attr.rs:65:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks + --> $DIR/pin_v2-attr.rs:67:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on extern crates + --> $DIR/pin_v2-attr.rs:71:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on use statements + --> $DIR/pin_v2-attr.rs:74:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on statics + --> $DIR/pin_v2-attr.rs:77:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on constants + --> $DIR/pin_v2-attr.rs:80:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on functions + --> $DIR/pin_v2-attr.rs:83:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:84:12 + | +LL | fn f(#[pin_v2] param: Foo) + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on closures + --> $DIR/pin_v2-attr.rs:92:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on expressions + --> $DIR/pin_v2-attr.rs:94:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:98:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on statements + --> $DIR/pin_v2-attr.rs:96:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on match arms + --> $DIR/pin_v2-attr.rs:102:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on pattern fields + --> $DIR/pin_v2-attr.rs:106:13 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on where predicates + --> $DIR/pin_v2-attr.rs:88:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on modules + --> $DIR/pin_v2-attr.rs:112:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign modules + --> $DIR/pin_v2-attr.rs:115:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign types + --> $DIR/pin_v2-attr.rs:117:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign statics + --> $DIR/pin_v2-attr.rs:120:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign functions + --> $DIR/pin_v2-attr.rs:123:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on type aliases + --> $DIR/pin_v2-attr.rs:127:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on macro defs + --> $DIR/pin_v2-attr.rs:130:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: aborting due to 39 previous errors +