diff options
| author | The Miri Cronjob Bot <miri@cron.bot> | 2024-07-27 05:22:40 +0000 |
|---|---|---|
| committer | The Miri Cronjob Bot <miri@cron.bot> | 2024-07-27 05:22:40 +0000 |
| commit | 00e89d3cb6c549d195c41557ee467ddc559b3efe (patch) | |
| tree | 1facc011e12f747c51718d5acb5dd4f0f9719ed6 | |
| parent | 80a32f86187f897cc075a314cd689c6bd45c4671 (diff) | |
| parent | a526d7ce45fd2284e0e7c7556ccba2425b9d25e5 (diff) | |
| download | rust-00e89d3cb6c549d195c41557ee467ddc559b3efe.tar.gz rust-00e89d3cb6c549d195c41557ee467ddc559b3efe.zip | |
Merge from rustc
437 files changed, 6831 insertions, 6168 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1dec7d0c11e..f89ecc92add 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,7 +573,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clippy" -version = "0.1.81" +version = "0.1.82" dependencies = [ "anstream", "clippy_config", @@ -594,13 +594,13 @@ dependencies = [ "termize", "tokio", "toml 0.7.8", - "ui_test 0.23.0", + "ui_test 0.24.0", "walkdir", ] [[package]] name = "clippy_config" -version = "0.1.81" +version = "0.1.82" dependencies = [ "rustc-semver", "serde", @@ -623,7 +623,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.81" +version = "0.1.82" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -648,7 +648,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.81" +version = "0.1.82" dependencies = [ "arrayvec", "clippy_config", @@ -969,7 +969,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.81" +version = "0.1.82" dependencies = [ "itertools", "quote", @@ -2978,6 +2978,16 @@ dependencies = [ ] [[package]] +name = "prettydiff" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abec3fb083c10660b3854367697da94c674e9e82aa7511014dc958beeb7215e9" +dependencies = [ + "owo-colors", + "pad", +] + +[[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5825,7 +5835,7 @@ dependencies = [ "indicatif", "lazy_static", "levenshtein", - "prettydiff", + "prettydiff 0.6.4", "regex", "rustc_version", "rustfix 0.6.1", @@ -5836,9 +5846,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e5f4ffcbab82453958fbf59990e981b8e8a177dcd60c2bd8f9b52c3036a6e1" +checksum = "bc1c6c78d55482388711c8d417b8e547263046a607512278fed274c54633bbe4" dependencies = [ "annotate-snippets 0.11.4", "anyhow", @@ -5852,14 +5862,13 @@ dependencies = [ "indicatif", "lazy_static", "levenshtein", - "prettydiff", + "prettydiff 0.7.0", "regex", "rustc_version", "rustfix 0.8.1", "serde", "serde_json", "spanned", - "tempfile", ] [[package]] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f990b4ba69b..6e6aac1ddfc 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -172,7 +172,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, hir_id: hir::HirId, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis_span: Span, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); @@ -566,7 +566,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `ItemLocalId` and the new owner. (See `lower_node_id`) let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); - if let Some(attrs) = attrs { + if !attrs.is_empty() { this.attrs.insert(hir::ItemLocalId::ZERO, attrs); } @@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> { continue; } let is_param = *is_param.get_or_insert_with(compute_is_param); - if !is_param { - self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() }); + if !is_param && !self.tcx.features().more_maybe_bounds { + self.tcx + .sess + .create_feature_err( + MisplacedRelaxTraitBound { span: bound.span() }, + sym::more_maybe_bounds, + ) + .emit(); } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0f5f4d8023b..d44f953010a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -913,15 +913,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> { + fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] { if attrs.is_empty() { - None + &[] } else { debug_assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); debug_assert!(!ret.is_empty()); self.attrs.insert(id.local_id, ret); - Some(ret) + ret } } @@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx, TraitBoundModifiers::NONE, ); + let bound = (bound, hir::TraitBoundModifier::None); let bounds = this.arena.alloc_from_iter([bound]); let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) @@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We can safely ignore constness here since AST validation // takes care of rejecting invalid modifier combinations and // const trait bounds in trait object types. - GenericBound::Trait(ty, modifiers) => match modifiers.polarity { - BoundPolarity::Positive | BoundPolarity::Negative(_) => { - Some(this.lower_poly_trait_ref( - ty, - itctx, - // Still, don't pass along the constness here; we don't want to - // synthesize any host effect args, it'd only cause problems. - TraitBoundModifiers { - constness: BoundConstness::Never, - ..*modifiers - }, - )) - } - BoundPolarity::Maybe(_) => None, - }, + GenericBound::Trait(ty, modifiers) => { + // Still, don't pass along the constness here; we don't want to + // synthesize any host effect args, it'd only cause problems. + let modifiers = TraitBoundModifiers { + constness: BoundConstness::Never, + ..*modifiers + }; + let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers); + let polarity = this.lower_trait_bound_modifiers(modifiers); + Some((trait_ref, polarity)) + } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(this.lower_lifetime(lifetime)); @@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { trait_ref: hir::TraitRef { path, hir_ref_id: hir_id }, span: self.lower_span(span), }; + let principal = (principal, hir::TraitBoundModifier::None); // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1088db74cc9..9b063a330b7 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match bound { GenericBound::Trait(trait_ref, modifiers) => { match (ctxt, modifiers.constness, modifiers.polarity) { - (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitSupertrait { - span: trait_ref.span, - path_str: pprust::path_to_string(&trait_ref.trait_ref.path), - }); + (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitSupertrait { + span: trait_ref.span, + path_str: pprust::path_to_string(&trait_ref.trait_ref.path), + }, + sym::more_maybe_bounds, + ) + .emit(); } - (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span }); + (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitObject { span: trait_ref.span }, + sym::more_maybe_bounds, + ) + .emit(); } ( BoundKind::TraitObject, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 2d9bc45ebc8..e5bc4b38748 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1442,9 +1442,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { // See `tests/ui/moves/needs-clone-through-deref.rs` return false; } + // We don't want to suggest `.clone()` in a move closure, since the value has already been captured. if self.in_move_closure(expr) { return false; } + // We also don't want to suggest cloning a closure itself, since the value has already been captured. + if let hir::ExprKind::Closure(_) = expr.kind { + return false; + } // Try to find predicates on *generic params* that would allow copying `ty` let mut suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 9b5ed3b0876..d3d07181096 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -205,6 +205,8 @@ declare_features! ( (unstable, lifetime_capture_rules_2024, "1.76.0", None), /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 (unstable, link_cfg, "1.14.0", None), + /// Allows using `?Trait` trait bounds in more contexts. + (internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None), /// Allows the `multiple_supertrait_upcastable` lint. (unstable, multiple_supertrait_upcastable, "1.69.0", None), /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 20b15499234..8c8f760bc41 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> { OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + TraitObject( + &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)], + &'hir Lifetime, + TraitObjectSyntax, + ), /// Unused for now. Typeof(&'hir AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index c202ee41e31..696f548f1ba 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul try_visit!(visitor.visit_array_length(length)); } TyKind::TraitObject(bounds, ref lifetime, _syntax) => { - walk_list!(visitor, visit_poly_trait_ref, bounds); + for (bound, _modifier) in bounds { + try_visit!(visitor.visit_poly_trait_ref(bound)); + } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 58cc0f62111..1821387e85f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -162,6 +162,7 @@ language_item_table! { StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; + CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the `DiscriminantKind` trait. diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index cc404daa51f..367f6e17e7f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,4 +1,4 @@ -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}` .label = ambiguous associated {$assoc_kind} `{$assoc_name}` hir_analysis_ambiguous_lifetime_bound = @@ -12,16 +12,21 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private .label = private {$kind} .defined_here_label = the {$kind} is defined here -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> [true] an *[false] a similarly named } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found -hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = + consider fully qualifying{$identically_named -> + [true] {""} + *[false] {" "}and renaming + } the associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` -hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name +hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0316ef69bf8..456dc4e4e07 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { - hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { + hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments { [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 349dc9ad00e..02ea95852f0 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - for bound in bounds { + for (bound, _) in bounds { this.visit_poly_trait_ref_inner( bound, NonLifetimeBinderAllowed::Deny("trait object types"), diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c83788928a9..c364a561631 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> { pub span: Span, pub assoc_kind: &'static str, pub assoc_name: Ident, - pub ty_param_name: &'a str, + pub qself: &'a str, } #[derive(Diagnostic)] @@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> { pub span: Span, pub assoc_name: Ident, pub assoc_kind: &'static str, - pub ty_param_name: &'a str, + pub qself: &'a str, #[subdiagnostic] pub label: Option<AssocItemNotFoundLabel<'a>>, #[subdiagnostic] @@ -126,13 +126,32 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, - #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")] + #[multipart_suggestion( + hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, + style = "verbose" + )] + SimilarInOtherTraitQPath { + #[suggestion_part(code = "<")] + lo: Span, + #[suggestion_part(code = " as {trait_ref}>")] + mi: Span, + #[suggestion_part(code = "{suggested_name}")] + hi: Option<Span>, + trait_ref: String, + suggested_name: Symbol, + identically_named: bool, + #[applicability] + applicability: Applicability, + }, + #[suggestion( + hir_analysis_assoc_item_not_found_other_sugg, + code = "{suggested_name}", + applicability = "maybe-incorrect" + )] Other { #[primary_span] span: Span, - #[applicability] - applicability: Applicability, - ty_param_name: &'a str, + qself: &'a str, assoc_kind: &'static str, suggested_name: Symbol, }, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6f9c481650b..30c04aa47a3 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -6,19 +6,17 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; -use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{sym, ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; - -use super::RegionInferReason; +use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -75,10 +73,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let mut unique_bounds = FxIndexSet::default(); + let mut seen_repeat = false; + for unbound in &unbounds { + if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res { + seen_repeat |= !unique_bounds.insert(unbound_def_id); + } + } if unbounds.len() > 1 { - self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { + let err = errors::MultipleRelaxedDefaultBounds { spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }); + }; + if seen_repeat { + self.dcx().emit_err(err); + } else if !tcx.features().more_maybe_bounds { + self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit(); + }; } let mut seen_sized_unbound = false; @@ -288,8 +298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // one that does define it. self.probe_single_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), - trait_ref.skip_binder().print_only_trait_name(), - None, + AssocItemQSelf::Trait(trait_ref.def_id()), assoc_kind, constraint.ident, path_span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 8ff6ced8b39..20f06d77489 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,7 +3,7 @@ use crate::errors::{ ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; -use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; @@ -11,9 +11,9 @@ use rustc_errors::MultiSpan; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, }; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, Node}; +use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::query::Key; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; @@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn complain_about_assoc_item_not_found<I>( &self, all_candidates: impl Fn() -> I, - ty_param_name: &str, - ty_param_def_id: Option<LocalDefId>, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - let assoc_kind_str = super::assoc_kind_str(assoc_kind); + let assoc_kind_str = assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // valid span, so we point at the whole path segment instead. @@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: if is_dummy { span } else { assoc_name.span }, assoc_name, assoc_kind: assoc_kind_str, - ty_param_name, + qself: &qself_str, label: None, sugg: None, }; @@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggested_name, identically_named: suggested_name == assoc_name.name, }); - let hir = tcx.hir(); - if let Some(def_id) = ty_param_def_id - && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) - && let Some(generics) = hir.get_generics(parent.def_id) + if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself + // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're + // inside an opaque type while we're interested in the overarching type alias (TAIT). + // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... + && let item_def_id = + tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id)) + // FIXME: ...which obviously won't have any generics. + && let Some(generics) = tcx.hir().get_generics(item_def_id.def_id) { - if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( - |b| match b { + // FIXME: Suggest adding supertrait bounds if we have a `Self` type param. + // FIXME(trait_alias): Suggest adding `Self: Trait` to + // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`. + if generics + .bounds_for_param(ty_param_def_id) + .flat_map(|pred| pred.bounds.iter()) + .any(|b| match b { hir::GenericBound::Trait(t, ..) => { t.trait_ref.trait_def_id() == Some(best_trait) } _ => false, - }, - ) { + }) + { // The type param already has a bound for `trait_name`, we just need to // change the associated item. err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { @@ -242,48 +251,60 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return self.dcx().emit_err(err); } - let mut err = self.dcx().create_err(err); - if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &ty_param_name, - &trait_name, - None, - None, - ) && suggested_name != assoc_name.name + let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..]; + let mut trait_ref = trait_name.clone(); + let applicability = if let [arg, args @ ..] = trait_args { + use std::fmt::Write; + write!(trait_ref, "</* {arg}").unwrap(); + args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap(); + trait_ref += " */>"; + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + + let identically_named = suggested_name == assoc_name.name; + + if let DefKind::TyAlias = tcx.def_kind(item_def_id) + && !tcx.type_alias_is_lazy(item_def_id) { - // We suggested constraining a type parameter, but the associated item on it - // was also not an exact match, so we also suggest changing it. - err.span_suggestion_verbose( - assoc_name.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { + lo: ty_param_span.shrink_to_lo(), + mi: ty_param_span.shrink_to_hi(), + hi: (!identically_named).then_some(assoc_name.span), + trait_ref, + identically_named, suggested_name, - Applicability::MaybeIncorrect, - ); + applicability, + }); + } else { + let mut err = self.dcx().create_err(err); + if suggest_constraining_type_param( + tcx, generics, &mut err, &qself_str, &trait_ref, None, None, + ) && !identically_named + { + // We suggested constraining a type parameter, but the associated item on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + suggested_name, + Applicability::MaybeIncorrect, + ); + } + return err.emit(); } - return err.emit(); } return self.dcx().emit_err(err); } } // If we still couldn't find any associated item, and only one associated item exists, - // suggests using it. + // suggest using it. if let [candidate_name] = all_candidate_names.as_slice() { - // This should still compile, except on `#![feature(associated_type_defaults)]` - // where it could suggests `type A = Self::A`, thus recursing infinitely. - let applicability = - if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults { - Applicability::Unspecified - } else { - Applicability::MaybeIncorrect - }; - err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_name.span, - applicability, - ty_param_name, + qself: &qself_str, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, }); @@ -349,10 +370,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx().emit_err(errors::AssocKindMismatch { span, - expected: super::assoc_kind_str(expected), - got: super::assoc_kind_str(got), + expected: assoc_kind_str(expected), + got: assoc_kind_str(got), expected_because_label, - assoc_kind: super::assoc_kind_str(assoc_item.kind), + assoc_kind: assoc_kind_str(assoc_item.kind), def_span: tcx.def_span(assoc_item.def_id), bound_on_assoc_const_label, wrap_in_braces_sugg, @@ -698,7 +719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, associated_types: FxIndexMap<Span, FxIndexSet<DefId>>, potential_assoc_types: Vec<usize>, - trait_bounds: &[hir::PolyTraitRef<'_>], + trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)], ) { if associated_types.values().all(|v| v.is_empty()) { return; @@ -744,12 +765,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // related to issue #91997, turbofishes added only when in an expr or pat let mut in_expr_or_pat = false; if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { - let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); + let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { - Node::Expr(_) | Node::Pat(_) => true, + hir::Node::Expr(_) | hir::Node::Pat(_) => true, _ => false, }; - match bound.trait_ref.path.segments { + match bound.0.trait_ref.path.segments { // FIXME: `trait_ref.path.span` can point to a full path with multiple // segments, even though `trait_ref.path.segments` is of length `1`. Work // around that bug here, even though it should be fixed elsewhere. @@ -790,7 +811,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // and we can then use their span to indicate this to the user. let bound_names = trait_bounds .iter() - .filter_map(|poly_trait_ref| { + .filter_map(|(poly_trait_ref, _)| { let path = poly_trait_ref.trait_ref.path.segments.last()?; let args = path.args?; @@ -1612,3 +1633,11 @@ fn generics_args_err_extend<'a>( _ => {} } } + +pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { + match kind { + ty::AssocKind::Fn => "function", + ty::AssocKind::Const => "constant", + ty::AssocKind::Type => "type", + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 29c71c3fa50..e7aad0a29c5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .ok() .is_some_and(|s| s.trim_end().ends_with('<')); - let is_global = poly_trait_ref.trait_ref.path.is_global(); + let is_global = poly_trait_ref.0.trait_ref.path.is_global(); let mut sugg = vec![( self_ty.span.shrink_to_lo(), @@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut is_downgradable = true; let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { + objects.iter().all(|(o, _)| match o.trait_ref.path.res { Res::Def(DefKind::Trait, id) => { if Some(id) == owner { // For recursive traits, don't downgrade the error. (#119652) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index d6eb1a66902..ce298641e60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use std::fmt::Display; use std::slice; /// A path segment that is semantically allowed to have generic arguments. @@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> { } } +/// The "qualified self" of an associated item path. +/// +/// For diagnostic purposes only. +enum AssocItemQSelf { + Trait(DefId), + TyParam(LocalDefId, Span), + SelfTyAlias, +} + +impl AssocItemQSelf { + fn to_string(&self, tcx: TyCtxt<'_>) -> String { + match *self { + Self::Trait(def_id) => tcx.def_path_str(def_id), + Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(), + Self::SelfTyAlias => kw::SelfUpper.to_string(), + } + } +} + /// New-typed boolean indicating whether explicit late-bound lifetimes /// are present in a set of generic arguments. /// @@ -802,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_single_ty_param_bound_for_assoc_ty( &self, ty_param_def_id: LocalDefId, + ty_param_span: Span, assoc_name: Ident, span: Span, ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { @@ -811,19 +830,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; debug!("predicates={:#?}", predicates); - let param_name = tcx.hir().ty_param_name(ty_param_def_id); self.probe_single_bound_for_assoc_item( || { - traits::transitive_bounds_that_define_assoc_item( - tcx, - predicates - .iter() - .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), - assoc_name, - ) + let trait_refs = predicates + .iter() + .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); + traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - param_name, - Some(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), ty::AssocKind::Type, assoc_name, span, @@ -835,12 +849,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] + #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)] fn probe_single_bound_for_assoc_item<I>( &self, all_candidates: impl Fn() -> I, - ty_param_name: impl Display, - ty_param_def_id: Option<LocalDefId>, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -858,8 +871,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let Some(bound) = matching_candidates.next() else { let reported = self.complain_about_assoc_item_not_found( all_candidates, - &ty_param_name.to_string(), - ty_param_def_id, + qself, assoc_kind, assoc_name, span, @@ -872,13 +884,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(bound2) = matching_candidates.next() { debug!(?bound2); - let assoc_kind_str = assoc_kind_str(assoc_kind); - let ty_param_name = &ty_param_name.to_string(); + let assoc_kind_str = errors::assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, assoc_name, - ty_param_name, + qself: &qself_str, }); // Provide a more specific error code index entry for equality bindings. err.code( @@ -929,7 +941,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), "use fully-qualified syntax to disambiguate", - format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), + format!("<{qself_str} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, ); } @@ -943,7 +955,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if !where_bounds.is_empty() { err.help(format!( "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {ty_param_name},\n{}", + \n where\n T: {qself_str},\n{}", where_bounds.join(",\n"), )); } @@ -997,11 +1009,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let assoc_ident = assoc_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err - }; // Check if we have an enum variant or an inherent associated type. let mut variant_resolution = None; @@ -1038,6 +1045,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + path.res + } else { + Res::Err + }; + // Find the type of the associated item, and the trait where the associated // item is declared. let bound = match (&qself_ty.kind(), qself_res) { @@ -1056,8 +1069,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Binder::dummy(trait_ref.instantiate_identity()), ) }, - kw::SelfUpper, - None, + AssocItemQSelf::SelfTyAlias, ty::AssocKind::Type, assoc_ident, span, @@ -1069,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.probe_single_ty_param_bound_for_assoc_ty( param_did.expect_local(), + qself.span, assoc_ident, span, )?, @@ -2522,11 +2535,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(r) } } - -fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { - match kind { - ty::AssocKind::Fn => "function", - ty::AssocKind::Const => "constant", - ty::AssocKind::Type => "type", - } -} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index aafadc7f9cb..b3c7a1ff8a8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], + hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)], lifetime: &hir::Lifetime, borrowed: bool, representation: DynKind, @@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in hir_trait_bounds.iter().rev() { + for (trait_bound, modifier) in hir_trait_bounds.iter().rev() { + if *modifier == hir::TraitBoundModifier::Maybe { + continue; + } if let GenericArgCountResult { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), @@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let args = tcx.mk_args(&args); let span = i.bottom().1; - let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a982b84e755..e25f43e169d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -300,13 +300,16 @@ impl<'a> State<'a> { self.word_space("dyn"); } let mut first = true; - for bound in bounds { + for (bound, modifier) in bounds { if first { first = false; } else { self.nbsp(); self.word_space("+"); } + if *modifier == TraitBoundModifier::Maybe { + self.word("?"); + } self.print_poly_trait_ref(bound); } if !lifetime.is_elided() { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index a87b3c2c135..987dbf6db63 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -144,13 +144,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters -lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - -lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases - .suggestion = the bound will not be checked when the type alias is used, and should be removed - -lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases - .suggestion = the clause will not be checked when the type alias is used, and should be removed +lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias +lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information +lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced + .suggestion = remove {$count -> + [one] this bound + *[other] these bounds + } +lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type +lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced + .suggestion = remove this where clause lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9ebada0fff3..ab0b47d48e5 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -31,12 +31,11 @@ use crate::{ BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes, + BuiltinWhileTrue, InvalidAsmLabel, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; @@ -1391,64 +1390,80 @@ declare_lint! { /// /// ### Explanation /// - /// The trait bounds in a type alias are currently ignored, and should not - /// be included to avoid confusion. This was previously allowed - /// unintentionally; this may become a hard error in the future. + /// Trait and lifetime bounds on generic parameters and in where clauses of + /// type aliases are not checked at usage sites of the type alias. Moreover, + /// they are not thoroughly checked for correctness at their definition site + /// either similar to the aliased type. + /// + /// This is a known limitation of the type checker that may be lifted in a + /// future edition. Permitting such bounds in light of this was unintentional. + /// + /// While these bounds may have secondary effects such as enabling the use of + /// "shorthand" associated type paths[^1] and affecting the default trait + /// object lifetime[^2] of trait object types passed to the type alias, this + /// should not have been allowed until the aforementioned restrictions of the + /// type checker have been lifted. + /// + /// Using such bounds is highly discouraged as they are actively misleading. + /// + /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter + /// bounded by trait `Trait` which defines an associated type called `Assoc` + /// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`. + /// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes> TYPE_ALIAS_BOUNDS, Warn, "bounds in type aliases are not enforced" } -declare_lint_pass!( - /// Lint for trait and lifetime bounds in type aliases being mostly ignored. - /// They are relevant when using associated types, but otherwise neither checked - /// at definition site nor enforced at use site. - TypeAliasBounds => [TYPE_ALIAS_BOUNDS] -); +declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]); impl TypeAliasBounds { - pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { - match *qpath { - hir::QPath::TypeRelative(ty, _) => { - // If this is a type variable, we found a `T::Assoc`. - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { - matches!(path.res, Res::Def(DefKind::TyParam, _)) - } - _ => false, - } - } - hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, + pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { + // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults. + if let hir::WherePredicate::BoundPredicate(pred) = pred + && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) + && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS + && pred.bounded_ty.as_generic_param().is_some() + { + return true; } + false } } impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; + let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return }; - // Bounds of lazy type aliases and TAITs are respected. - if cx.tcx.type_alias_is_lazy(item.owner_id) { + // There must not be a where clause. + if generics.predicates.is_empty() { return; } - let ty = cx.tcx.type_of(item.owner_id).skip_binder(); - if ty.has_inherent_projections() { - // Bounds of type aliases that contain opaque types or inherent projections are - // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = - // Type::Inherent;`. + // Bounds of lazy type aliases and TAITs are respected. + if cx.tcx.type_alias_is_lazy(item.owner_id) { return; } - // There must not be a where clause - if type_alias_generics.predicates.is_empty() { + // FIXME(generic_const_exprs): Revisit this before stabilization. + // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + && cx.tcx.features().generic_const_exprs + { return; } + // NOTE(inherent_associated_types): While we currently do take some bounds in type + // aliases into consideration during IAT *selection*, we don't perform full use+def + // site wfchecking for such type aliases. Therefore TAB should still trigger. + // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`. + let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); - for p in type_alias_generics.predicates { + + for p in generics.predicates { let span = p.span(); if p.in_where_clause() { where_spans.push(span); @@ -1460,37 +1475,57 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } } - let mut suggested_changing_assoc_types = false; - if !where_spans.is_empty() { - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + let mut ty = Some(hir_ty); + let enable_feat_help = cx.tcx.sess.is_nightly_build(); + + if let [.., label_sp] = *where_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasWhereClause { - suggestion: type_alias_generics.where_clause_span, - sub, + BuiltinTypeAliasBounds { + in_where_clause: true, + label: label_sp, + enable_feat_help, + suggestions: vec![(generics.where_clause_span, String::new())], + preds: generics.predicates, + ty: ty.take(), }, ); } - - if !inline_spans.is_empty() { - let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + if let [.., label_sp] = *inline_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, inline_spans, - BuiltinTypeAliasGenericBounds { suggestion, sub }, + BuiltinTypeAliasBounds { + in_where_clause: false, + label: label_sp, + enable_feat_help, + suggestions: inline_sugg, + preds: generics.predicates, + ty, + }, ); } } } +pub(crate) struct ShorthandAssocTyCollector { + pub(crate) qselves: Vec<Span>, +} + +impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector { + fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { + // Look for "type-parameter shorthand-associated-types". I.e., paths of the + // form `T::Assoc` with `T` type param. These are reliant on trait bounds. + if let hir::QPath::TypeRelative(qself, _) = qpath + && qself.as_generic_param().is_some() + { + self.qselves.push(qself.span); + } + hir::intravisit::walk_qpath(self, qpath, id) + } +} + declare_lint! { /// The `trivial_bounds` lint detects trait bounds that don't depend on /// any type parameters. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1f0954c6e9f..b669a3c6288 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2,14 +2,16 @@ #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; -use crate::errors::RequestedLevel; +use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; +use crate::errors::{OverruledAttributeSub, RequestedLevel}; use crate::fluent_generated as fluent; +use crate::LateContext; use rustc_errors::{ codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, }; -use rustc_hir::{def::Namespace, def_id::DefId}; +use rustc_hir::{self as hir, def::Namespace, def_id::DefId}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, @@ -22,10 +24,6 @@ use rustc_span::{ Span, Symbol, }; -use crate::{ - builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, -}; - // array_into_iter.rs #[derive(LintDiagnostic)] #[diag(lint_shadowed_into_iter)] @@ -263,62 +261,6 @@ pub struct BuiltinUnreachablePub<'a> { pub help: Option<()>, } -pub struct SuggestChangingAssocTypes<'a, 'b> { - pub ty: &'a rustc_hir::Ty<'b>, -} - -impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - // Access to associates types should use `<T as Bound>::Assoc`, which does not need a - // bound. Let's see if this type does that. - - // We use a HIR visitor to walk the type. - use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> { - err: &'a mut Diag<'b, G>, - } - impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> { - fn visit_qpath( - &mut self, - qpath: &rustc_hir::QPath<'_>, - id: rustc_hir::HirId, - span: Span, - ) { - if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help); - } - intravisit::walk_qpath(self, qpath, id) - } - } - - // Let's go for a walk! - let mut visitor = WalkAssocTypes { err: diag }; - visitor.visit_ty(self.ty); - } -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_where_clause)] -pub struct BuiltinTypeAliasWhereClause<'a, 'b> { - #[suggestion(code = "", applicability = "machine-applicable")] - pub suggestion: Span, - #[subdiagnostic] - pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_generic_bounds)] -pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { - #[subdiagnostic] - pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion, - #[subdiagnostic] - pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, -} - #[derive(LintDiagnostic)] #[diag(lint_macro_expr_fragment_specifier_2024_migration)] pub struct MacroExprFragment2024 { @@ -326,21 +268,72 @@ pub struct MacroExprFragment2024 { pub suggestion: Span, } -pub struct BuiltinTypeAliasGenericBoundsSuggestion { +pub struct BuiltinTypeAliasBounds<'a, 'hir> { + pub in_where_clause: bool, + pub label: Span, + pub enable_feat_help: bool, pub suggestions: Vec<(Span, String)>, + pub preds: &'hir [hir::WherePredicate<'hir>], + pub ty: Option<&'a hir::Ty<'hir>>, } -impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.multipart_suggestion( - fluent::lint_suggestion, - self.suggestions, - Applicability::MachineApplicable, - ); +impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(if self.in_where_clause { + fluent::lint_builtin_type_alias_bounds_where_clause + } else { + fluent::lint_builtin_type_alias_bounds_param_bounds + }); + diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label); + diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note); + if self.enable_feat_help { + diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help); + } + + // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to + // avoid doing throwaway work in case the lint ends up getting suppressed. + let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; + if let Some(ty) = self.ty { + hir::intravisit::Visitor::visit_ty(&mut collector, ty); + } + + let affect_object_lifetime_defaults = self + .preds + .iter() + .filter(|pred| pred.in_where_clause() == self.in_where_clause) + .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred)); + + // If there are any shorthand assoc tys, then the bounds can't be removed automatically. + // The user first needs to fully qualify the assoc tys. + let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + + diag.arg("count", self.suggestions.len()); + diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability); + + // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via + // `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds. + // + // Instead of attempting to figure out the necessary trait ref, just use a + // placeholder. Since we don't record type-dependent resolutions for non-body + // items like type aliases, we can't simply deduce the corresp. trait from + // the HIR path alone without rerunning parts of HIR ty lowering here + // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. + // + // (We could employ some simple heuristics but that's likely not worth it). + for qself in collector.qselves { + diag.multipart_suggestion( + fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + vec![ + (qself.shrink_to_lo(), "<".into()), + (qself.shrink_to_hi(), " as /* Trait */>".into()), + ], + Applicability::HasPlaceholders, + ); + } } } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 2f8eea6cd18..93c606a954e 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -429,7 +429,7 @@ fn ty_has_local_parent( path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent( - principle_poly_trait_ref.trait_ref.path, + principle_poly_trait_ref.0.trait_ref.path, cx, impl_parent, impl_parent_parent, @@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span .to_string(), ), TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { - let path = &principle_poly_trait_ref.trait_ref.path; + let path = &principle_poly_trait_ref.0.trait_ref.path; ( path_span_without_args(path), path.res diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 6983e7abbd6..552245f0cdd 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; - for bound in &bounds[..] { + for (bound, modifier) in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if cx.tcx.lang_items().drop_trait() == def_id { + if cx.tcx.lang_items().drop_trait() == def_id + && *modifier != hir::TraitBoundModifier::Maybe + { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d2b444a066b..da98e3b9f46 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1864,9 +1864,9 @@ impl<'tcx> Ty<'tcx> { // Definitely absolutely not copy. ty::Ref(_, _, hir::Mutability::Mut) => false, - // Thin pointers & thin shared references are pure-clone-copy, but for - // anything with custom metadata it might be more complicated. - ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false, + // The standard library has a blanket Copy impl for shared references and raw pointers, + // for all unsized types. + ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => true, ty::Coroutine(..) | ty::CoroutineWitness(..) => false, diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 8209e5e2711..58fdc2d9e45 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -3,6 +3,7 @@ use crate::simplify::simplify_duplicate_switch_targets; use crate::take_array; use rustc_ast::attr; +use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::layout; @@ -271,8 +272,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { return; } - let trait_def_id = self.tcx.trait_of_item(fn_def_id); - if trait_def_id.is_none() || trait_def_id != self.tcx.lang_items().clone_trait() { + if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) { return; } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6835a39cf36..d2f50040821 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,7 +17,7 @@ use std::iter; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, - mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, + instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; @@ -154,6 +154,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, + &instsimplify::InstSimplify, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], @@ -434,6 +435,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - match self_ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(), ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()), + ty::CoroutineClosure(_, args) => { + builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys()) + } ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()), ty::Coroutine(coroutine_def_id, args) => { assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 770ac9a929e..60beaa0df84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -217,7 +217,10 @@ where // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), - ty::CoroutineClosure(..) => Err(NoSolution), + // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone + ty::CoroutineClosure(_, args) => { + Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())]) + } // only when `coroutine_clone` is enabled and the coroutine is movable // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 0b2c3044039..535b53a836e 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -457,14 +457,3 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(err)) } } - -/// The attributes are complete if all attributes are either a doc comment or a -/// builtin attribute other than `cfg_attr`. -pub fn is_complete(attrs: &[ast::Attribute]) -> bool { - attrs.iter().all(|attr| { - attr.is_doc_comment() - || attr.ident().is_some_and(|ident| { - ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) - }) - }) -} diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dc5f98f7be8..5dc49ea51d1 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -60,10 +60,6 @@ impl AttrWrapper { pub fn is_empty(&self) -> bool { self.attrs.is_empty() } - - pub fn is_complete(&self) -> bool { - crate::parser::attr::is_complete(&self.attrs) - } } /// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute @@ -114,17 +110,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { replace_ranges.sort_by_key(|(range, _)| range.start); #[cfg(debug_assertions)] - { - for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { - assert!( - range.end <= next_range.start || range.end >= next_range.end, - "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", - range, - tokens, - next_range, - next_tokens, - ); - } + for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + assert!( + range.end <= next_range.start || range.end >= next_range.end, + "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + range, + tokens, + next_range, + next_tokens, + ); } // Process the replace ranges, starting from the highest start @@ -137,9 +131,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // By starting processing from the replace range with the greatest - // start position, we ensure that any replace range which encloses - // another replace range will capture the *replaced* tokens for the inner - // range, not the original tokens. + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. for (range, target) in replace_ranges.into_iter().rev() { assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); @@ -199,20 +193,20 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>, ) -> PResult<'a, R> { - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens (because force collection - // requires tokens by definition). - if matches!(force_collect, ForceCollect::No) - // - None of our outer attributes require tokens. - && attrs.is_complete() - // - Our target doesn't support custom inner attributes (custom + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens (because force collection requires + // tokens by definition). + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer attributes require tokens. + || needs_tokens(&attrs.attrs) + // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). - && !R::SUPPORTS_CUSTOM_INNER_ATTRS - // - We are not in `capture_cfg` mode (which requires tokens if + || R::SUPPORTS_CUSTOM_INNER_ATTRS + // - We are in `capture_cfg` mode (which requires tokens if // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). - && !self.capture_cfg - { + || self.capture_cfg; + if !needs_collection { return Ok(f(self, attrs.attrs)?.0); } @@ -250,28 +244,28 @@ impl<'a> Parser<'a> { return Ok(ret); } - // This is similar to the "skip collection" check at the start of this - // function, but now that we've parsed an AST node we have more + // This is similar to the `needs_collection` check at the start of this + // function, but now that we've parsed an AST node we have complete // information available. (If we return early here that means the // setup, such as cloning the token cursor, was unnecessary. That's // hard to avoid.) // - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens. - if matches!(force_collect, ForceCollect::No) - // - None of our outer *or* inner attributes require tokens. - // (`attrs` was just outer attributes, but `ret.attrs()` is outer - // and inner attributes. That makes this check more precise than - // `attrs.is_complete()` at the start of the function, and we can - // skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`. - && crate::parser::attr::is_complete(ret.attrs()) - // - We are not in `capture_cfg` mode, or we are but there are no - // `#[cfg]` or `#[cfg_attr]` attributes. (During normal - // non-`capture_cfg` parsing, we don't need any special capturing - // for those attributes, because they're builtin.) - && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs())) - { + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens. + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer *or* inner attributes require tokens. + // (`attr.attrs` was just outer attributes, but `ret.attrs()` is + // outer and inner attributes. So this check is more precise than + // the earlier `needs_tokens` check, and we don't need to + // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) + || needs_tokens(ret.attrs()) + // - We are in `capture_cfg` mode and there are `#[cfg]` or + // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` + // parsing, we don't need any special capturing for those + // attributes, because they're builtin.) + || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())); + if !needs_collection { return Ok(ret); } @@ -297,11 +291,13 @@ impl<'a> Parser<'a> { // with `None`, which means the relevant tokens will be removed. (More // details below.) let mut inner_attr_replace_ranges = Vec::new(); - for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { - inner_attr_replace_ranges.push((attr_range, None)); - } else { - self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); + for attr in ret.attrs() { + if attr.style == ast::AttrStyle::Inner { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) { + inner_attr_replace_ranges.push((attr_range, None)); + } else { + self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute"); + } } } @@ -337,8 +333,7 @@ impl<'a> Parser<'a> { // When parsing `m`: // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). // - `inner_attr_replace_ranges` is empty. - // - `replace_range_start..replace_ranges_end` has two entries. - // - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above). + // - `replace_range_start..replace_ranges_end` has one entry. // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, // including its outer attribute), with: // - `attrs`: includes the outer and the inner attr. @@ -369,12 +364,10 @@ impl<'a> Parser<'a> { // What is the status here when parsing the example code at the top of this method? // - // When parsing `g`, we add two entries: + // When parsing `g`, we add one entry: // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: // - `attrs`: includes the outer and the inner attr. // - `tokens`: lazy tokens for `g` (with its inner attr deleted). - // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's - // tokens (`17..27`). // // When parsing `m`, we do nothing here. @@ -384,7 +377,6 @@ impl<'a> Parser<'a> { let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); - self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } else if matches!(self.capture_state.capturing, Capturing::No) { // Only clear the ranges once we've finished capturing entirely, i.e. we've finished // the outermost call to this method. @@ -461,6 +453,19 @@ fn make_attr_token_stream( AttrTokenStream::new(stack_top.inner) } +/// Tokens are needed if: +/// - any non-single-segment attributes (other than doc comments) are present; or +/// - any `cfg_attr` attributes are present; +/// - any single-segment, non-builtin attributes are present. +fn needs_tokens(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| match attr.ident() { + None => !attr.is_doc_comment(), + Some(ident) => { + ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name) + } + }) +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9aaf4b99243..112855e6d1f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> { /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { + const ALL_QUALS: &[Symbol] = + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]; + // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] + ALL_QUALS } else { &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] }; @@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> { || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_string_literal()) && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) || - // this branch is only for better diagnostic in later, `pub` is not allowed here + // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here (self.may_recover() - && self.look_ahead(2, |t| t.is_keyword(kw::Pub)) + && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw))) && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case)))) } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 55514883cb1..239bc8e7acc 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -54,7 +54,24 @@ impl Publicness { } } -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { +fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { + match ty.kind { + TyKind::Path(hir::QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + { + Some((local_def_id, def_kind)) + } else { + None + } + } + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), + _ => None, + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness @@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { /// for enum and union, just check they are public, /// and doesn't solve types like &T for now, just skip them fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - { + if let Some((def_id, def_kind)) = adt_of(ty) { return match def_kind { DefKind::Enum | DefKind::Union => { let ty_is_public = tcx.visibility(def_id).is_public(); @@ -565,10 +579,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir().item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() + if let Some((local_def_id, def_kind)) = + adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id @@ -915,7 +927,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 046ae5fc593..df80f4df5b9 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1448,7 +1448,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ); if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span }); + let label_span = ident.span.shrink_to_hi(); + let mut spans = MultiSpan::from_span(label_span); + spans.push_span_label(label_span, "put a macro name here"); + err.subdiagnostic(MaybeMissingMacroRulesName { spans: spans }); return; } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 0a68231c6fe..698147765b3 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -669,7 +669,7 @@ pub(crate) struct MacroSuggMovePosition { #[note(resolve_missing_macro_rules_name)] pub(crate) struct MaybeMissingMacroRulesName { #[primary_span] - pub(crate) span: Span, + pub(crate) spans: MultiSpan, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2b30ca8a894..cfd263d5951 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -557,6 +557,7 @@ symbols! { clobber_abi, clone, clone_closures, + clone_fn, clone_from, closure, closure_lifetime_binder, @@ -1229,6 +1230,7 @@ symbols! { modifiers, module, module_path, + more_maybe_bounds, more_qualified_paths, more_struct_aliases, movbe_target_feature, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index b91b755d683..a892ce58861 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } hir::TyKind::TraitObject(bounds, ..) => { - for bound in bounds { + for (bound, _) in bounds { self.current_index.shift_in(1); self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index ce157ff3dc8..e5d97976534 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { _, ) = t.kind { - for ptr in poly_trait_refs { + for (ptr, _) in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { self.0.push(ptr.span); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 87fdc5ddff8..89ae6f4ccab 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>( if tcx.parent_hir_node(hir_id).fn_sig().is_some() { // Do not suggest `impl Trait` when dealing with things like super-traits. err.span_suggestion_verbose( - ty.span.until(trait_ref.span), + ty.span.until(trait_ref.0.span), "consider using an opaque type instead", "impl ", Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 885216e6216..d1381d24764 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match ty.kind { hir::TyKind::TraitObject(traits, _, _) => { let (span, kw) = match traits { - [first, ..] if first.span.lo() == ty.span.lo() => { + [(first, _), ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. (ty.span.shrink_to_lo(), "dyn ") } - [first, ..] => (ty.span.until(first.span), ""), + [(first, _), ..] => (ty.span.until(first.span), ""), [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c007cd5314a..699c05466bd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // FIXME(async_closures): These are never clone, for now. - ty::CoroutineClosure(_, _) => None, + ty::CoroutineClosure(_, args) => { + // (*) binder moved here + let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where( + obligation + .predicate + .rebind(args.as_coroutine_closure().upvar_tys().to_vec()), + ) + } + } + // `Copy` and `Clone` are automatically implemented for an anonymous adt // if all of its fields are `Copy` and `Clone` ty::Adt(adt, args) if adt.is_anonymous() => { diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6ec38b78fc2..d5e114b2175 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> { self.has_type_flags(TypeFlags::HAS_ALIAS) } - fn has_inherent_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INHERENT) - } - fn has_opaque_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } diff --git a/config.example.toml b/config.example.toml index 26687bcfb37..8800c85db32 100644 --- a/config.example.toml +++ b/config.example.toml @@ -395,7 +395,7 @@ #metrics = false # Specify the location of the Android NDK. Used when targeting Android. -#android-ndk = "/path/to/android-ndk-r25b" +#android-ndk = "/path/to/android-ndk-r26d" # ============================================================================= # General install configuration options diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 939b2be6dfa..76a89eaaff8 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -160,6 +160,9 @@ pub trait Clone: Sized { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] + // Clone::clone is special because the compiler generates MIR to implement it for some types. + // See InstanceKind::CloneShim. + #[cfg_attr(not(bootstrap), lang = "clone_fn")] fn clone(&self) -> Self; /// Performs copy-assignment from `source`. diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 36fae1c1596..794ca1851b1 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -402,6 +402,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { } } +/// A helper used to print list-like items with no special formatting. struct DebugInner<'a, 'b: 'a> { fmt: &'a mut fmt::Formatter<'b>, result: fmt::Result, @@ -578,7 +579,8 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.result.and_then(|_| self.inner.fmt.write_str("}")) + self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("}")); + self.inner.result } } @@ -721,7 +723,8 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.result.and_then(|_| self.inner.fmt.write_str("]")) + self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("]")); + self.inner.result } } @@ -1002,11 +1005,12 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.result.and_then(|_| { + self.result = self.result.and_then(|_| { assert!(!self.has_key, "attempted to finish a map with a partial entry"); self.fmt.write_str("}") - }) + }); + self.result } fn is_pretty(&self) -> bool { diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 733d414d444..469097e4847 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -823,7 +823,7 @@ pub trait Iterator { /// /// Given an element the closure must return `true` or `false`. The returned /// iterator will yield only the elements for which the closure returns - /// true. + /// `true`. /// /// # Examples /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 02cb02dce1d..9f0055d1911 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -127,7 +127,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index a2d7e6f7b07..b8e22a8aef9 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,7 +113,7 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "int_error_matching", since = "1.55.0")] pub const fn kind(&self) -> &IntErrorKind { &self.kind diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4e8e0ecdde9..3442bb71417 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1386,6 +1386,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] +#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize } @@ -1435,7 +1436,7 @@ macro_rules! from_str_radix { #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -1565,7 +1566,7 @@ macro_rules! from_str_radix_size_impl { #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { match <$t>::from_str_radix(src, radix) { Ok(x) => Ok(x as $size), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 04aaa3f35b7..4c225187882 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -16,7 +16,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] -#![feature(const_int_from_str)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_pointer_is_aligned)] diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 14bce2bbeee..9a3d95bd8dd 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -29,6 +29,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl<T: ?Sized> Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 714643c8386..2514eb00344 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -17,6 +17,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl<T: ?Sized> Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 20d79e490ec..e947e3735db 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -247,10 +247,8 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path triple.to_string() }; - // API 19 is the earliest API level supported by NDK r25b but AArch64 and x86_64 support - // begins at API level 21. - let api_level = - if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" }; + // The earliest API supported by NDK r26d is 21. + let api_level = "21"; let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang()); let host_tag = if cfg!(target_os = "macos") { // The NDK uses universal binaries, so this is correct even on ARM. @@ -258,7 +256,7 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path } else if cfg!(target_os = "windows") { "windows-x86_64" } else { - // NDK r25b only has official releases for macOS, Windows and Linux. + // NDK r26d only has official releases for macOS, Windows and Linux. // Try the Linux directory everywhere else, on the assumption that the OS has an // emulation layer that can cope (e.g. BSDs). "linux-x86_64" diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 083418ed066..879cc079c6b 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -210,4 +210,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "the `wasm-component-ld` tool is now built as part of `build.extended` and can be a member of `build.tools`", }, + ChangeInfo { + change_id: 120593, + severity: ChangeSeverity::Info, + summary: "Removed android-ndk r25b support in favor of android-ndk r26d.", + }, ]; diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index abca06fb9fb..222fa8a7355 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -6,7 +6,7 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_ndk android-ndk-r25b-linux.zip + download_ndk android-ndk-r26d-linux.zip RUN dpkg --add-architecture i386 && \ apt-get update && \ diff --git a/src/ci/docker/host-x86_64/arm-android/android-sdk.lock b/src/ci/docker/host-x86_64/arm-android/android-sdk.lock index a1be8a4346b..33b0c66ae09 100644 --- a/src/ci/docker/host-x86_64/arm-android/android-sdk.lock +++ b/src/ci/docker/host-x86_64/arm-android/android-sdk.lock @@ -1,6 +1,6 @@ emulator emulator-linux-5264690.zip 48c1cda2bdf3095d9d9d5c010fbfb3d6d673e3ea patcher;v4 3534162-studio.sdk-patcher.zip 046699c5e2716ae11d77e0bad814f7f33fab261e -platform-tools platform-tools_r28.0.2-linux.zip 46a4c02a9b8e4e2121eddf6025da3c979bf02e28 -platforms;android-18 android-18_r03.zip e6b09b3505754cbbeb4a5622008b907262ee91cb -system-images;android-18;default;armeabi-v7a sys-img/android/armeabi-v7a-18_r05.zip 580b583720f7de671040d5917c8c9db0c7aa03fd +platform-tools platform-tools_r34.0.5-linux.zip 96097475cf7b279fdd8f218f5d043ffe94104ec3 +platforms;android-21 android-21_r02.zip 53536556059bb29ae82f414fd2e14bc335a4eb4c +system-images;android-21;default;armeabi-v7a sys-img/android/armeabi-v7a-21_r04.zip 8c606f81306564b65e41303d2603e4c42ded0d10 tools sdk-tools-linux-4333796.zip 8c7c28554a32318461802c1291d76fccfafde054 diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index 20b72b377ca..54649e0d22b 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -6,7 +6,7 @@ RUN sh /scripts/android-base-apt-get.sh # ndk COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_ndk android-ndk-r25b-linux.zip + download_ndk android-ndk-r26d-linux.zip # env ENV TARGETS=arm-linux-androideabi diff --git a/src/ci/docker/scripts/android-start-emulator.sh b/src/ci/docker/scripts/android-start-emulator.sh index 09f0d13759c..5ffb72eddb1 100755 --- a/src/ci/docker/scripts/android-start-emulator.sh +++ b/src/ci/docker/scripts/android-start-emulator.sh @@ -10,7 +10,7 @@ export SHELL=/bin/bash # the emulator date is set to unix epoch (in armeabi-v7a-18 image). Using # classic engine the emulator starts with the current date and the tests run # fine. If another image is used, this need to be evaluated again. -nohup nohup emulator @armeabi-v7a-18 \ +nohup nohup emulator @armeabi-v7a-21 \ -engine classic -no-window -partition-size 2047 0<&- &>/dev/null & exec "$@" diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/calculate-job-matrix.py index d03bbda1008..7de6d5fcd5f 100755 --- a/src/ci/github-actions/calculate-job-matrix.py +++ b/src/ci/github-actions/calculate-job-matrix.py @@ -97,9 +97,15 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: "refs/heads/automation/bors/try" ) + # Unrolled branch from a rollup for testing perf + # This should **not** allow custom try jobs + is_unrolled_perf_build = ctx.ref == "refs/heads/try-perf" + if try_build: - jobs = get_custom_jobs(ctx) - return TryRunType(custom_jobs=jobs) + custom_jobs = [] + if not is_unrolled_perf_build: + custom_jobs = get_custom_jobs(ctx) + return TryRunType(custom_jobs=custom_jobs) if ctx.ref == "refs/heads/auto": return AutoRunType() diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2cd9b6fcd38..26011926cdd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, ref lifetime, _) => { - let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); + let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 86af38f3ee7..3eb27ea087c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2932,7 +2932,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\ } // Update document title to maintain a meaningful browser history - searchState.title = "Results for " + query.original + " - Rust"; + searchState.title = "\"" + query.original + "\" Search - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index 48a63e48568..7afdd068a99 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -4,7 +4,7 @@ uibless = "test --test compile-test -- -- --bless" bless = "test -- -- --bless" dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " -collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored" +collect-metadata = "test --test dogfood --features internal -- collect_metadata" [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index f016a770059..6a5139b6dc0 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -53,18 +53,18 @@ jobs: id: cache-json uses: actions/cache@v4 with: - path: lintcheck-logs/lintcheck_crates_logs.json + path: lintcheck-logs/ci_crates_logs.json key: ${{ steps.key.outputs.key }} - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --warn-all + run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 with: name: base - path: lintcheck-logs/lintcheck_crates_logs.json + path: lintcheck-logs/ci_crates_logs.json # Runs lintcheck on the PR and stores the results as an artifact head: @@ -86,13 +86,13 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --warn-all + run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 with: name: head - path: lintcheck-logs/lintcheck_crates_logs.json + path: lintcheck-logs/ci_crates_logs.json # Retrieves the head and base JSON results and prints the diff to the GH actions step summary diff: @@ -115,4 +115,20 @@ jobs: uses: actions/download-artifact@v4 - name: Diff results - run: ./target/debug/lintcheck diff {base,head}/lintcheck_crates_logs.json >> $GITHUB_STEP_SUMMARY + # GH's summery has a maximum size of 1024k: + # https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary + # That's why we first log to file and then to the summary and logs + run: | + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md + head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY + cat truncated_diff.md + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md + + - name: Upload full diff + uses: actions/upload-artifact@v4 + with: + name: diff + if-no-files-found: ignore + path: | + full_diff.md + truncated_diff.md diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 55281f3cbec..60c03b03d9b 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,53 @@ document. ## Unreleased / Beta / In Rust Nightly -[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master) +[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master) + +## Rust 1.80 + +Current stable, released 2024-07-25 + +[View all 68 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-04-18T22%3A50%3A22Z..2024-05-30T08%3A26%3A18Z+base%3Amaster) + +### New Lints + +* Added [`while_float`] to `nursery` + [#12765](https://github.com/rust-lang/rust-clippy/pull/12765) +* Added [`macro_metavars_in_unsafe`] to `suspicious` + [#12107](https://github.com/rust-lang/rust-clippy/pull/12107) +* Added [`renamed_function_params`] to `restriction` + [#11540](https://github.com/rust-lang/rust-clippy/pull/11540) +* Added [`doc_lazy_continuation`] to `style` + [#12770](https://github.com/rust-lang/rust-clippy/pull/12770) + +### Moves and Deprecations + +* Moved [`assigning_clones`] to `pedantic` (From `perf` now allow-by-default) + [#12779](https://github.com/rust-lang/rust-clippy/pull/12779) +* Moved [`single_char_pattern`] to `pedantic` (From `perf` now allow-by-default) + [#11852](https://github.com/rust-lang/rust-clippy/pull/11852) + +### Enhancements + +* [`panic`]: Added [`allow-panic-in-tests`] configuration to allow the lint in tests + [#12803](https://github.com/rust-lang/rust-clippy/pull/12803) +* [`missing_const_for_fn`]: Now respects the [`msrv`] configuration + [#12713](https://github.com/rust-lang/rust-clippy/pull/12713) +* [`missing_panics_doc`]: No longer lints on compile-time panics + [#12790](https://github.com/rust-lang/rust-clippy/pull/12790) +* [`collapsible_match`]: Now considers the [`msrv`] configuration for the suggestion + [#12745](https://github.com/rust-lang/rust-clippy/pull/12745) +* [`useless_vec`]: Added [`allow-useless-vec-in-tests`] configuration to allow the lint in tests + [#12725](https://github.com/rust-lang/rust-clippy/pull/12725) + +### Suggestion Fixes/Improvements + +* [`single_match`], [`single_match_else`]: Suggestions are now machine-applicable + [#12726](https://github.com/rust-lang/rust-clippy/pull/12726) ## Rust 1.79 -Current stable, released 2024-06-13 +Released 2024-06-13 [View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster) @@ -5712,6 +5754,7 @@ Released 2018-09-13 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext +[`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 43788499055..bb4dc97e748 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.81" +version = "0.1.82" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -30,11 +30,10 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.23" +ui_test = "0.24" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" -# This is used by the `collect-metadata` alias. filetime = "0.2.9" itertools = "0.12" @@ -63,3 +62,7 @@ rustc_private = true [[test]] name = "compile-test" harness = false + +[[test]] +name = "dogfood" +harness = false diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 48c00bcbf34..a71d94daca7 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -458,9 +458,8 @@ pub struct ManualStrip { } impl ManualStrip { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv.clone() } } } ``` @@ -689,7 +688,6 @@ for some users. Adding a configuration is done in the following steps: ]); // New manual definition struct - #[derive(Copy, Clone)] pub struct StructName {} impl_lint_pass!(StructName => [ @@ -700,7 +698,6 @@ for some users. Adding a configuration is done in the following steps: 2. Next add the configuration value and a corresponding creation method like this: ```rust - #[derive(Copy, Clone)] pub struct StructName { configuration_ident: Type, } @@ -708,9 +705,9 @@ for some users. Adding a configuration is done in the following steps: // ... impl StructName { - pub fn new(configuration_ident: Type) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - configuration_ident, + configuration_ident: conf.configuration_ident, } } } @@ -726,8 +723,7 @@ for some users. Adding a configuration is done in the following steps: store.register_*_pass(|| box module::StructName); // New registration with configuration value - let configuration_ident = conf.configuration_ident.clone(); - store.register_*_pass(move || box module::StructName::new(configuration_ident)); + store.register_*_pass(move || box module::StructName::new(conf)); ``` Congratulations the work is almost done. The configuration value can now be diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index ad29339a84a..fb717a2c166 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]` --- **Affected lints:** @@ -679,6 +679,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) * [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) * [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) +* [`collapsible_match`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match) * [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) * [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) * [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index 319b72e8c5d..a7b0cc56ea1 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -8,7 +8,6 @@ reason = "this function does not add a link to our documentation, please use the path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" - [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index be0b048ac0c..e1b2edc8a6f 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.81" +version = "0.1.82" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 7f53aad6793..63140a36875 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -18,23 +18,26 @@ use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "AccessKit", + "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", - "DirectX", + "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", - "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", + "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", - "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", - "WebGL", "WebGL2", "WebGPU", + "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", + "OpenType", + "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", - "iOS", "macOS", "FreeBSD", + "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", @@ -235,7 +238,7 @@ define_Conf! { /// /// A type, say `SomeType`, listed in this configuration has the same behavior of /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. - (arithmetic_side_effects_allowed: FxHashSet<String> = <_>::default()), + (arithmetic_side_effects_allowed: Vec<String> = <_>::default()), /// Lint: ARITHMETIC_SIDE_EFFECTS. /// /// Suppress checking of the passed type pair names in binary operations like addition or @@ -262,12 +265,12 @@ define_Conf! { /// ```toml /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] /// ``` - (arithmetic_side_effects_allowed_unary: FxHashSet<String> = <_>::default()), + (arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()), /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH. /// /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = ""] @@ -311,7 +314,7 @@ define_Conf! { /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. - (doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), + (doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. /// /// The maximum number of argument a function or method can have @@ -547,7 +550,7 @@ define_Conf! { /// Lint: PATH_ENDS_WITH_EXT. /// /// Additional dotfiles (files or directories starting with a dot) to allow - (allowed_dotfiles: FxHashSet<String> = FxHashSet::default()), + (allowed_dotfiles: Vec<String> = Vec::default()), /// Lint: MULTIPLE_CRATE_VERSIONS. /// /// A list of crate names to allow duplicates of @@ -700,7 +703,6 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> { fn deserialize(file: &SourceFile) -> TryConf { match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) { Ok(mut conf) => { - extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES); extend_vec_if_indicator_present( @@ -713,6 +715,11 @@ fn deserialize(file: &SourceFile) -> TryConf { .allowed_idents_below_min_chars .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string)); } + if conf.conf.doc_valid_idents.contains("..") { + conf.conf + .doc_valid_idents + .extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string)); + } conf }, diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 435aa9244c5..d47e34bb5bc 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -2,13 +2,13 @@ use serde::de::{self, Deserializer, Visitor}; use serde::{ser, Deserialize, Serialize}; use std::fmt; -#[derive(Clone, Debug, Deserialize)] +#[derive(Debug, Deserialize)] pub struct Rename { pub path: String, pub rename: String, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(untagged)] pub enum DisallowedPath { Simple(String), @@ -22,12 +22,10 @@ impl DisallowedPath { path } - pub fn reason(&self) -> Option<String> { - match self { - Self::WithReason { - reason: Some(reason), .. - } => Some(format!("{reason} (from clippy.toml)")), - _ => None, + pub fn reason(&self) -> Option<&str> { + match &self { + Self::WithReason { reason, .. } => reason.as_deref(), + Self::Simple(_) => None, } } } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index d762e30ef02..de91233d196 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -140,7 +140,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let new_lint = if enable_msrv { format!( - "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ", + "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ", lint_pass = lint.pass, ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, @@ -274,6 +274,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { formatdoc!( r#" use clippy_config::msrvs::{{self, Msrv}}; + use clippy_config::Conf; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_session::impl_lint_pass; @@ -301,9 +302,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }} impl {name_camel} {{ - #[must_use] - pub fn new(msrv: Msrv) -> Self {{ - Self {{ msrv }} + pub fn new(conf: &'static Conf) -> Self {{ + Self {{ msrv: conf.msrv.clone() }} }} }} diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 5708ffba08f..eb04c006f89 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.81" +version = "0.1.82" 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/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index 461117cf965..c0a9d888e0b 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; use rustc_data_structures::fx::FxHashSet; @@ -47,7 +48,16 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { pub absolute_paths_max_segments: u64, - pub absolute_paths_allowed_crates: FxHashSet<String>, + pub absolute_paths_allowed_crates: &'static FxHashSet<String>, +} + +impl AbsolutePaths { + pub fn new(conf: &'static Conf) -> Self { + Self { + absolute_paths_max_segments: conf.absolute_paths_max_segments, + absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates, + } + } } impl LateLintPass<'_> for AbsolutePaths { diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs index 96e9c949b75..451bae95987 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{trim_span, walk_span_to_context}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; @@ -34,8 +35,10 @@ pub struct AlmostCompleteRange { msrv: Msrv, } impl AlmostCompleteRange { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } impl EarlyLintPass for AlmostCompleteRange { diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index ec28fd46111..e6d52bcef71 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; @@ -67,9 +68,10 @@ pub struct ApproxConstant { } impl ApproxConstant { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index d57ab539fff..4eafa330faf 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_from_proc_macro, last_path_segment}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -42,12 +42,11 @@ declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() - && let ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, ty, sym::Arc) - && let ExprKind::Call(func, [arg]) = expr.kind - && let ExprKind::Path(func_path) = func.kind - && last_path_segment(&func_path).ident.name == sym::new + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind + && func_name.ident.name == sym::new + && !expr.span.from_expansion() + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 0de0031ed24..03f777600f0 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; use clippy_utils::sugg::Sugg; @@ -57,9 +58,10 @@ pub struct AssigningClones { } impl AssigningClones { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 8ec60314cc9..8f430ae601a 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -16,6 +16,7 @@ mod useless_attribute; mod utils; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; @@ -499,7 +500,6 @@ declare_clippy_lint! { "duplicated attribute" } -#[derive(Clone)] pub struct Attributes { msrv: Msrv, } @@ -517,9 +517,10 @@ impl_lint_pass!(Attributes => [ ]); impl Attributes { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -589,7 +590,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } pub struct EarlyAttributes { - pub msrv: Msrv, + msrv: Msrv, +} + +impl EarlyAttributes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } } impl_lint_pass!(EarlyAttributes => [ diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index d4a1e2780d0..d5f017b2650 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -1,11 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; -use rustc_data_structures::fx::FxHashMap; +use clippy_utils::{create_disallowed_map, match_def_path, paths}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{sym, Span}; @@ -172,31 +172,19 @@ declare_clippy_lint! { impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); -#[derive(Debug)] pub struct AwaitHolding { - conf_invalid_types: Vec<DisallowedPath>, - def_ids: FxHashMap<DefId, DisallowedPath>, + def_ids: DefIdMap<(&'static str, Option<&'static str>)>, } impl AwaitHolding { - pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self { + pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_invalid_types, - def_ids: FxHashMap::default(), + def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types), } } } impl<'tcx> LateLintPass<'tcx> for AwaitHolding { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for conf in &self.conf_invalid_types { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.def_ids.insert(id, conf.clone()); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)), @@ -258,25 +246,22 @@ impl AwaitHolding { ); }, ); - } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.source_info.span, disallowed); + } else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.source_info.span, path, reason); } } } } } -fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) { +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, reason: Option<&'static str>) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - format!( - "`{}` may not be held across an await point per `clippy.toml`", - disallowed.path() - ), + format!("holding a disallowed type across an await point `{path}`"), |diag| { - if let Some(reason) = disallowed.reason() { + if let Some(reason) = reason { diag.note(reason); } }, diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 0ca4a0e067d..bd123a725a7 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -49,35 +49,31 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if !e.span.from_expansion() - && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind - && !addrof_target.span.from_expansion() + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind - && !deref_target.span.from_expansion() && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) + && !e.span.from_expansion() + && !deref_target.span.from_expansion() + && !addrof_target.span.from_expansion() && let ref_ty = cx.typeck_results().expr_ty(deref_target) && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() - { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) - && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) - { - return; + && get_parent_expr(cx, e).map_or(true, |parent| { + match parent.kind { + // `*&*foo` should lint `deref_addrof` instead. + ExprKind::Unary(UnOp::Deref, _) => is_lint_allowed(cx, DEREF_ADDROF, parent.hir_id), + // `&*foo` creates a distinct temporary from `foo` + ExprKind::AddrOf(_, Mutability::Mut, _) => !matches!( + deref_target.kind, + ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..) + ), + _ => true, } - - // modification to `&mut &*x` is different from `&mut x` - if matches!( - deref_target.kind, - ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..) - ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) - { - return; - } - } - if is_from_proc_macro(cx, e) { - return; - } - + }) + && !is_from_proc_macro(cx, e) + { span_lint_and_then( cx, BORROW_DEREF_REF, diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 593bc6c81ee..312ad4c2990 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -5,6 +5,7 @@ mod multiple_crate_versions; mod wildcard_dependencies; use cargo_metadata::MetadataCommand; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashSet; @@ -204,8 +205,8 @@ declare_clippy_lint! { } pub struct Cargo { - pub allowed_duplicate_crates: FxHashSet<String>, - pub ignore_publish: bool, + allowed_duplicate_crates: &'static FxHashSet<String>, + ignore_publish: bool, } impl_lint_pass!(Cargo => [ @@ -217,6 +218,15 @@ impl_lint_pass!(Cargo => [ LINT_GROUPS_PRIORITY, ]); +impl Cargo { + pub fn new(conf: &'static Conf) -> Self { + Self { + allowed_duplicate_crates: &conf.allowed_duplicate_crates, + ignore_publish: conf.cargo_ignore_publish, + } + } +} + impl LateLintPass<'_> for Cargo { fn check_crate(&mut self, cx: &LateContext<'_>) { static NO_DEPS_LINTS: &[&Lint] = &[ @@ -253,7 +263,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); + multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index d52ad1c6f23..ff460a3fd8e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,19 +1,21 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath, TyKind}; +use rustc_hir::{Expr, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty, UintTy}; +use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_span::hygiene; use super::{utils, CAST_LOSSLESS}; pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, - cast_op: &Expr<'_>, + cast_from_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, cast_to_hir: &rustc_hir::Ty<'_>, @@ -23,64 +25,54 @@ pub(super) fn check( return; } - // The suggestion is to use a function call, so if the original expression - // has parens on the outside, they are no longer needed. - let mut app = Applicability::MachineApplicable; - let opt = snippet_opt(cx, cast_op.span.source_callsite()); - let sugg = opt.as_ref().map_or_else( - || { - app = Applicability::HasPlaceholders; - ".." - }, - |snip| { - if should_strip_parens(cast_op, snip) { - &snip[1..snip.len() - 1] - } else { - snip.as_str() - } - }, - ); - - // Display the type alias instead of the aliased type. Fixes #11285 - // - // FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead, - // this will allow us to display the right type with `cast_from` as well. - let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind - // It's a bit annoying but the turbofish is optional for types. A type in an `as` cast - // shouldn't have these if they're primitives, which are the only things we deal with. - // - // This could be removed for performance if this check is determined to have a pretty major - // effect. - && path.segments.iter().all(|segment| segment.args.is_none()) - { - snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app) - } else { - cast_to.to_string().into() - }; - - let message = if cast_from.is_bool() { - format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`") - } else { - format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type") - }; - - span_lint_and_sugg( + span_lint_and_then( cx, CAST_LOSSLESS, expr.span, - message, - "try", - format!("{cast_to_fmt}::from({sugg})"), - app, + format!("casts from `{cast_from}` to `{cast_to}` can be expressed infallibly using `From`"), + |diag| { + diag.help("an `as` cast can become silently lossy if the types change in the future"); + let mut applicability = Applicability::MachineApplicable; + let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability); + let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else { + return; + }; + match cast_to_hir.kind { + TyKind::Infer => { + diag.span_suggestion_verbose( + expr.span, + "use `Into::into` instead", + format!("{}.into()", from_sugg.maybe_par()), + applicability, + ); + }, + // Don't suggest `A<_>::B::From(x)` or `macro!()::from(x)` + kind if matches!(kind, TyKind::Path(QPath::Resolved(_, path)) if path.segments.iter().any(|s| s.args.is_some())) + || !cast_to_hir.span.eq_ctxt(expr.span) => + { + diag.span_suggestion_verbose( + expr.span, + format!("use `<{ty}>::from` instead"), + format!("<{ty}>::from({from_sugg})"), + applicability, + ); + }, + _ => { + diag.span_suggestion_verbose( + expr.span, + format!("use `{ty}::from` instead"), + format!("{ty}::from({from_sugg})"), + applicability, + ); + }, + } + }, ); } fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - // - // If destination is u128, do not lint because source type cannot be larger - // If source is bool, still lint due to the lint message differing (refers to style) - if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) { + if in_constant(cx, expr.hir_id) { return false; } @@ -110,12 +102,3 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to }, } } - -fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool { - if let ExprKind::Binary(_, _, _) = cast_expr.kind { - if snip.starts_with('(') && snip.ends_with(')') { - return true; - } - } - false -} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 54f0c7c4687..c31716fbcee 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -24,6 +24,7 @@ mod utils; mod zero_ptr; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::is_hir_ty_cfg_dependant; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -658,11 +659,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// let _: (0.0_f32 / 0.0) as u64; + /// let _ = (0.0_f32 / 0.0) as u64; /// ``` /// Use instead: /// ```rust,ignore - /// let _: = 0_u64; + /// let _ = 0_u64; /// ``` #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, @@ -722,9 +723,10 @@ pub struct Casts { } impl Casts { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -761,45 +763,45 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } - if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind { + if let ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to_hir) { return; } let (cast_from, cast_to) = ( - cx.typeck_results().expr_ty(cast_expr), + cx.typeck_results().expr_ty(cast_from_expr), cx.typeck_results().expr_ty(expr), ); - if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { + if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); - ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); - as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); - fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); - fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); - fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); - zero_ptr::check(cx, expr, cast_expr, cast_to_hir); + cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, &self.msrv); + ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); + fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); + fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); + zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); if cast_to.is_numeric() { - cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); + cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); - cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); + cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv); - cast_enum_constructor::check(cx, expr, cast_expr, cast_from); + cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, &self.msrv); + cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); if self.msrv.meets(msrvs::PTR_FROM_REF) { - ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } else if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 921693567fc..7513e18d408 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use super::PTR_CAST_CONSTNESS; @@ -24,6 +24,7 @@ pub(super) fn check<'tcx>( (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) ) && from_ty == to_ty + && !from_ty.has_erased_regions() { let sugg = Sugg::hir(cx, cast_expr, "_"); let constness = match *to_mutbl { diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 92810ea2aa0..0b1ab5411bf 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -1,11 +1,12 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -40,9 +41,10 @@ pub struct CheckedConversions { } impl CheckedConversions { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -50,61 +52,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !self.msrv.meets(msrvs::TRY_FROM) { - return; - } - - let result = if !in_constant(cx, item.hir_id) - && !in_external_macro(cx.sess(), item.span) - && let ExprKind::Binary(op, left, right) = &item.kind - { - match op.node { - BinOpKind::Ge | BinOpKind::Le => single_check(item), - BinOpKind::And => double_check(cx, left, right), - _ => None, + if let ExprKind::Binary(op, lhs, rhs) = item.kind + && let (lt1, gt1, op2) = match op.node { + BinOpKind::Le => (lhs, rhs, None), + BinOpKind::Ge => (rhs, lhs, None), + BinOpKind::And + if let ExprKind::Binary(op1, lhs1, rhs1) = lhs.kind + && let ExprKind::Binary(op2, lhs2, rhs2) = rhs.kind + && let Some((lt1, gt1)) = read_le_ge(op1.node, lhs1, rhs1) + && let Some((lt2, gt2)) = read_le_ge(op2.node, lhs2, rhs2) => + { + (lt1, gt1, Some((lt2, gt2))) + }, + _ => return, } - } else { - None - }; - - if let Some(cv) = result { - if let Some(to_type) = cv.to_type { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - CHECKED_CONVERSIONS, - item.span, - "checked cast can be simplified", - "try", - format!("{to_type}::try_from({snippet}).is_ok()"), - applicability, - ); + && !in_external_macro(cx.sess(), item.span) + && !in_constant(cx, item.hir_id) + && self.msrv.meets(msrvs::TRY_FROM) + && let Some(cv) = match op2 { + // todo: check for case signed -> larger unsigned == only x >= 0 + None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned), + Some((lt2, gt2)) => { + let upper_lower = |lt1, gt1, lt2, gt2| { + check_upper_bound(lt1, gt1) + .zip(check_lower_bound(lt2, gt2)) + .and_then(|(l, r)| l.combine(r, cx)) + }; + upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1)) + }, } + && let Some(to_type) = cv.to_type + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + CHECKED_CONVERSIONS, + item.span, + "checked cast can be simplified", + "try", + format!("{to_type}::try_from({snippet}).is_ok()"), + applicability, + ); } } extract_msrv_attr!(LateContext); } -/// Searches for a single check from unsigned to _ is done -/// todo: check for case signed -> larger unsigned == only x >= 0 -fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned) -} - -/// Searches for a combination of upper & lower bound checks -fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> { - let upper_lower = |l, r| { - let upper = check_upper_bound(l); - let lower = check_lower_bound(r); - - upper.zip(lower).and_then(|(l, r)| l.combine(r, cx)) - }; - - upper_lower(left, right).or_else(|| upper_lower(right, left)) -} - /// Contains the result of a tried conversion check #[derive(Clone, Debug)] struct Conversion<'a> { @@ -121,6 +116,19 @@ enum ConversionType { FromUnsigned, } +/// Attempts to read either `<=` or `>=` with a normalized operand order. +fn read_le_ge<'tcx>( + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + match op { + BinOpKind::Le => Some((lhs, rhs)), + BinOpKind::Ge => Some((rhs, lhs)), + _ => None, + } +} + impl<'a> Conversion<'a> { /// Combine multiple conversions if the are compatible pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> { @@ -188,29 +196,17 @@ impl ConversionType { } /// Check for `expr <= (to_type::MAX as from_type)` -fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - if let ExprKind::Binary(ref op, left, right) = &expr.kind - && let Some((candidate, check)) = normalize_le_ge(op, left, right) - && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX") - { - Conversion::try_new(candidate, from, to) +fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { + if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") { + Conversion::try_new(lt, from, to) } else { None } } /// Check for `expr >= 0|(to_type::MIN as from_type)` -fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> { - (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check))) - } - - // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, left, right) = &expr.kind { - normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) - } else { - None - } +fn check_lower_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { + check_lower_bound_zero(gt, lt).or_else(|| check_lower_bound_min(gt, lt)) } /// Check for `expr >= 0` @@ -309,15 +305,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { } } -/// Will return the expressions as if they were expr1 <= expr2 -fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { - match op.node { - BinOpKind::Le => Some((left, right)), - BinOpKind::Ge => Some((right, left)), - _ => None, - } -} - // Constants const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 60815f4f2af..5fa0522e4e5 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,6 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -39,10 +40,9 @@ pub struct CognitiveComplexity { } impl CognitiveComplexity { - #[must_use] - pub fn new(limit: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - limit: LimitStack::new(limit), + limit: LimitStack::new(conf.cognitive_complexity_threshold), } } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 07b02c98df1..f311c052ad6 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -93,20 +93,14 @@ declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { - check_if(cx, expr); - } - } -} - -fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::If(check, then, else_) = &expr.kind { - if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, then.span, else_); - } else if let ast::ExprKind::Let(..) = check.kind { - // Prevent triggering on `if let a = b { if c { .. } }`. - } else { - check_collapsible_no_if_let(cx, expr, check, then); + if let ast::ExprKind::If(cond, then, else_) = &expr.kind + && !expr.span.from_expansion() + { + if let Some(else_) = else_ { + check_collapsible_maybe_if_let(cx, then.span, else_); + } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { + check_collapsible_no_if_let(cx, expr, cond, then); + } } } } @@ -189,13 +183,10 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & /// If the block contains only one expression, return it. fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - let mut it = block.stmts.iter(); - - if let (Some(stmt), None) = (it.next(), it.next()) { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr), - _ => None, - } + if let [stmt] = &*block.stmts + && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind + { + Some(expr) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index 28d9f68d504..eebda3ff76f 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -59,9 +59,9 @@ static COLLECTIONS: [Symbol; 9] = [ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { - // Look for local variables whose type is a container. Search surrounding bock for read access. - if match_acceptable_type(cx, local, &COLLECTIONS) - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + // Look for local variables whose type is a container. Search surrounding block for read access. + if let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && match_acceptable_type(cx, local, &COLLECTIONS) && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) && has_no_read_access(cx, local_id, enclosing_block) { diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index d896452be92..86e0368c4e4 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt}; use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; @@ -11,6 +12,7 @@ use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; @@ -159,15 +161,13 @@ declare_clippy_lint! { } pub struct CopyAndPaste<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } -impl CopyAndPaste<'_> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { +impl<'tcx> CopyAndPaste<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), } } } @@ -180,10 +180,6 @@ impl_lint_pass!(CopyAndPaste<'_> => [ ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index adf6f7c4737..678bdbc0ffb 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -53,10 +53,9 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.attrs.iter().any(is_macro_export) - && let ItemKind::MacroDef(macro_def) = &item.kind - && let tts = macro_def.body.tokens.clone() - && let Some(span) = contains_unhygienic_crate_reference(&tts) + if let ItemKind::MacroDef(macro_def) = &item.kind + && item.attrs.iter().any(is_macro_export) + && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index b0590b0a71c..788c6f3ada2 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; @@ -33,7 +34,6 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -#[derive(Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, /// Tracks the `dbg!` macro callsites that are already checked. @@ -45,9 +45,9 @@ pub struct DbgMacro { impl_lint_pass!(DbgMacro => [DBG_MACRO]); impl DbgMacro { - pub fn new(allow_dbg_in_tests: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { DbgMacro { - allow_dbg_in_tests, + allow_dbg_in_tests: conf.allow_dbg_in_tests, checked_dbg_call_site: FxHashSet::default(), prev_ctxt: SyntaxContext::root(), } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index eabc67601a2..69f9eb6842b 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -598,6 +598,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, + crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::precedence::PRECEDENCE_INFO, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 0c9ad5e8d00..f27f68e2cbc 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; @@ -60,9 +61,10 @@ pub struct DerivableImpls { } impl DerivableImpls { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - DerivableImpls { msrv } + pub fn new(conf: &'static Conf) -> Self { + DerivableImpls { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 871f529da6c..b51d343132b 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -1,4 +1,5 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; +use clippy_utils::create_disallowed_map; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_ast::Attribute; @@ -9,6 +10,7 @@ use rustc_hir::{ Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{ExpnId, MacroKind, Span}; @@ -57,27 +59,24 @@ declare_clippy_lint! { } pub struct DisallowedMacros { - conf_disallowed: Vec<DisallowedPath>, - disallowed: DefIdMap<usize>, + disallowed: DefIdMap<(&'static str, Option<&'static str>)>, seen: FxHashSet<ExpnId>, - // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. derive_src: Option<OwnerId>, } impl DisallowedMacros { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_disallowed, - disallowed: DefIdMap::default(), + disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), seen: FxHashSet::default(), derive_src: None, } } fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId>) { - if self.conf_disallowed.is_empty() { + if self.disallowed.is_empty() { return; } @@ -86,11 +85,10 @@ impl DisallowedMacros { return; } - if let Some(&index) = self.disallowed.get(&mac.def_id) { - let conf = &self.conf_disallowed[index]; - let msg = format!("use of a disallowed macro `{}`", conf.path()); + if let Some(&(path, reason)) = self.disallowed.get(&mac.def_id) { + let msg = format!("use of a disallowed macro `{path}`"); let add_note = |diag: &mut Diag<'_, _>| { - if let Some(reason) = conf.reason() { + if let Some(reason) = reason { diag.note(reason); } }; @@ -116,15 +114,6 @@ impl DisallowedMacros { impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); impl LateLintPass<'_> for DisallowedMacros { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.disallowed.insert(id, index); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span, None); // `$t + $t` can have the context of $t, check also the span of the binary operator diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 38fe687f7cc..5a01d76a2a6 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,9 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; +use clippy_utils::create_disallowed_map; use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -55,17 +57,14 @@ declare_clippy_lint! { "use of a disallowed method call" } -#[derive(Clone, Debug)] pub struct DisallowedMethods { - conf_disallowed: Vec<DisallowedPath>, - disallowed: DefIdMap<usize>, + disallowed: DefIdMap<(&'static str, Option<&'static str>)>, } impl DisallowedMethods { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_disallowed, - disallowed: DefIdMap::default(), + disallowed: create_disallowed_map(tcx, &conf.disallowed_methods), } } } @@ -73,15 +72,6 @@ impl DisallowedMethods { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.disallowed.insert(id, index); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { ExprKind::Path(path) @@ -95,14 +85,18 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&index) = self.disallowed.get(&id) { - let conf = &self.conf_disallowed[index]; - let msg = format!("use of a disallowed method `{}`", conf.path()); - span_lint_and_then(cx, DISALLOWED_METHODS, span, msg, |diag| { - if let Some(reason) = conf.reason() { - diag.note(reason); - } - }); + if let Some(&(path, reason)) = self.disallowed.get(&id) { + span_lint_and_then( + cx, + DISALLOWED_METHODS, + span, + format!("use of a disallowed method `{path}`"), + |diag| { + if let Some(reason) = reason { + diag.note(reason); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs index 58809604c37..f55b0cf1c50 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs @@ -1,9 +1,11 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -24,15 +26,14 @@ declare_clippy_lint! { "usage of a disallowed/placeholder name" } -#[derive(Clone, Debug)] pub struct DisallowedNames { - disallow: FxHashSet<String>, + disallow: FxHashSet<Symbol>, } impl DisallowedNames { - pub fn new(disallowed_names: &[String]) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - disallow: disallowed_names.iter().cloned().collect(), + disallow: conf.disallowed_names.iter().map(|x| Symbol::intern(x)).collect(), } } } @@ -42,7 +43,7 @@ impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); impl<'tcx> LateLintPass<'tcx> for DisallowedNames { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { if let PatKind::Binding(.., ident, _) = pat.kind - && self.disallow.contains(&ident.name.to_string()) + && self.disallow.contains(&ident.name) && !is_in_test(cx.tcx, pat.hir_id) { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 5ce11900adf..f79264e6b04 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; @@ -44,19 +45,20 @@ declare_clippy_lint! { "usage of non-allowed Unicode scripts" } -#[derive(Clone, Debug)] pub struct DisallowedScriptIdents { whitelist: FxHashSet<Script>, } impl DisallowedScriptIdents { - pub fn new(whitelist: &[String]) -> Self { - let whitelist = whitelist - .iter() - .map(String::as_str) - .filter_map(Script::from_full_name) - .collect(); - Self { whitelist } + pub fn new(conf: &'static Conf) -> Self { + Self { + whitelist: conf + .allowed_scripts + .iter() + .map(String::as_str) + .filter_map(Script::from_full_name) + .collect(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 4196309a22a..3265404f2b2 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,10 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -49,60 +50,56 @@ declare_clippy_lint! { "use of disallowed types" } -#[derive(Clone, Debug)] pub struct DisallowedTypes { - conf_disallowed: Vec<DisallowedPath>, - def_ids: FxHashMap<DefId, usize>, - prim_tys: FxHashMap<PrimTy, usize>, + def_ids: DefIdMap<(&'static str, Option<&'static str>)>, + prim_tys: FxHashMap<PrimTy, (&'static str, Option<&'static str>)>, } impl DisallowedTypes { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { - Self { - conf_disallowed, - def_ids: FxHashMap::default(), - prim_tys: FxHashMap::default(), + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let mut def_ids = DefIdMap::default(); + let mut prim_tys = FxHashMap::default(); + for x in &conf.disallowed_types { + let path: Vec<_> = x.path().split("::").collect::<Vec<_>>(); + let reason = x.reason(); + for res in clippy_utils::def_path_res(tcx, &path) { + match res { + Res::Def(_, id) => { + def_ids.insert(id, (x.path(), reason)); + }, + Res::PrimTy(ty) => { + prim_tys.insert(ty, (x.path(), reason)); + }, + _ => {}, + } + } } + Self { def_ids, prim_tys } } fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - match res { - Res::Def(_, did) => { - if let Some(&index) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); - } - }, - Res::PrimTy(prim) => { - if let Some(&index) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); + let (path, reason) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + |diag| { + if let Some(reason) = reason { + diag.note(reason); } }, - _ => {}, - } + ); } } impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - - for res in clippy_utils::def_path_res(cx, &segs) { - match res { - Res::Def(_, id) => { - self.def_ids.insert(id, index); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, index); - }, - _ => {}, - } - } - } - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for res in &path.res { @@ -121,17 +118,3 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } - -fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &DisallowedPath) { - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("`{name}` is not allowed according to config"), - |diag| { - if let Some(reason) = conf.reason() { - diag.note(reason); - } - }, - ); -} diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index a2a1a51920f..5b6a5b08aa9 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1,4 +1,5 @@ mod lazy_continuation; +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -421,17 +422,16 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } -#[derive(Clone)] pub struct Documentation { - valid_idents: FxHashSet<String>, + valid_idents: &'static FxHashSet<String>, check_private_items: bool, } impl Documentation { - pub fn new(valid_idents: &[String], check_private_items: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - valid_idents: valid_idents.iter().cloned().collect(), - check_private_items, + valid_idents: &conf.doc_valid_idents, + check_private_items: conf.check_private_items, } } } @@ -452,7 +452,7 @@ impl_lint_pass!(Documentation => [ impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs index b51bb7951b7..4dd8f01ee70 100644 --- a/src/tools/clippy/clippy_lints/src/double_parens.rs +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -40,35 +40,29 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.from_expansion() { - return; - } - - let msg: &str = "consider removing unnecessary double parentheses"; - - match expr.kind { - ExprKind::Paren(ref in_paren) => match in_paren.kind { - ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, msg); - }, - _ => {}, - }, - ExprKind::Call(_, ref params) => { - if params.len() == 1 { - let param = ¶ms[0]; - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); - } - } + let span = match &expr.kind { + ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, + ExprKind::Call(_, params) + if let [param] = &**params + && let ExprKind::Paren(_) = param.kind => + { + param.span }, - ExprKind::MethodCall(ref call) => { - if let [ref arg] = call.args[..] { - if let ExprKind::Paren(_) = arg.kind { - span_lint(cx, DOUBLE_PARENS, arg.span, msg); - } - } + ExprKind::MethodCall(call) + if let [arg] = &*call.args + && let ExprKind::Paren(_) = arg.kind => + { + arg.span }, - _ => {}, + _ => return, + }; + if !expr.span.from_expansion() { + span_lint( + cx, + DOUBLE_PARENS, + span, + "consider removing unnecessary double parentheses", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index bb6f9aac223..7a6dc469727 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -50,12 +50,9 @@ declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if in_external_macro(cx.sess(), item.span) { - return; - } - if let ExprKind::If(_, _, Some(ref els)) = item.kind && let ExprKind::If(_, _, None) = els.kind + && !in_external_macro(cx.sess(), item.span) { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs index d16714695cb..1869faab1d3 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -64,25 +64,21 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - // Only suggest the `never_type` if the feature is enabled - if !cx.tcx.features().never_type { - return; - } - - if let ItemKind::Enum(..) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - if adt.variants().is_empty() { - span_lint_and_help( - cx, - EMPTY_ENUM, - item.span, - "enum with no variants", - None, - "consider using the uninhabited type `!` (never type) or a wrapper \ - around it to introduce a type which can't be instantiated", - ); - } + if let ItemKind::Enum(..) = item.kind + // Only suggest the `never_type` if the feature is enabled + && cx.tcx.features().never_type + && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def() + && adt.variants().is_empty() + { + span_lint_and_help( + cx, + EMPTY_ENUM, + item.span, + "enum with no variants", + None, + "consider using the uninhabited type `!` (never type) or a wrapper \ + around it to introduce a type which can't be instantiated", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index 99328e3e643..5bba9c562b9 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -109,32 +109,27 @@ impl LintKind { impl LateLintPass<'_> for EndianBytes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind - && args.is_empty() - && let ty = cx.typeck_results().expr_ty(receiver) + let (prefix, name, ty_expr) = match expr.kind { + ExprKind::MethodCall(method_name, receiver, [], ..) => (Prefix::To, method_name.ident.name, receiver), + ExprKind::Call(function, ..) + if let ExprKind::Path(qpath) = function.kind + && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() + && let Some(function_name) = cx.get_def_path(def_id).last() => + { + (Prefix::From, *function_name, expr) + }, + _ => return, + }; + if !in_external_macro(cx.sess(), expr.span) + && let ty = cx.typeck_results().expr_ty(ty_expr) && ty.is_primitive_ty() - && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty) { - return; - } - - if let ExprKind::Call(function, ..) = expr.kind - && let ExprKind::Path(qpath) = function.kind - && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() - && let Some(function_name) = cx.get_def_path(def_id).last() - && let ty = cx.typeck_results().expr_ty(expr) - && ty.is_primitive_ty() - { - maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); + maybe_lint_endian_bytes(cx, expr, prefix, name, ty); } } } -fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool { +fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) { let ne = LintKind::Host.as_name(prefix); let le = LintKind::Little.as_name(prefix); let be = LintKind::Big.as_name(prefix); @@ -143,7 +138,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]), name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]), name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]), - _ => return false, + _ => return, }; let mut help = None; @@ -208,6 +203,4 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix } }, ); - - true } diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 37442bf3e28..fb9f2b1526e 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -70,9 +70,9 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Let(let_expr) = expr.kind + if let ExprKind::Let(let_expr) = expr.kind && unary_pattern(let_expr.pat) + && !in_external_macro(cx.sess(), expr.span) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index 8e49138cd26..1e6447dc253 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -36,15 +36,12 @@ declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { - return; - }; - match item.kind { ItemKind::TyAlias(..) if item.ident.name == sym::Error && is_visible_outside_module(cx, item.owner_id.def_id) && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( @@ -56,9 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }, ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) - && let hir_id = cx.tcx.local_def_id_to_hir_id(def_id) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => @@ -66,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { span_lint_hir_and_then( cx, ERROR_IMPL_ERROR, - hir_id, + cx.tcx.local_def_id_to_hir_id(def_id), ident.span, "exported type named `Error` that implements `Error`", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 8d6e27700d8..a5da52b0be5 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -11,9 +12,16 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; -#[derive(Copy, Clone)] pub struct BoxedLocal { - pub too_large_for_stack: u64, + too_large_for_stack: u64, +} + +impl BoxedLocal { + pub fn new(conf: &'static Conf) -> Self { + Self { + too_large_for_stack: conf.too_large_for_stack, + } + } } declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 62d5ce24d40..8f469efb1b5 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; @@ -87,69 +88,57 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -enum Kind { - Struct, - Fn, -} - impl ExcessiveBools { - #[must_use] - pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - max_struct_bools, - max_fn_params_bools, + max_struct_bools: conf.max_struct_bools, + max_fn_params_bools: conf.max_fn_params_bools, } } +} - fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool { - if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { - (if Kind::Fn == kind { - self.max_fn_params_bools - } else { - self.max_struct_bools - }) < bools - } else { - false - } - } +impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); - fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { - if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { - span_lint_and_help( - cx, - FN_PARAMS_EXCESSIVE_BOOLS, - span, - format!("more than {} bools in function parameters", self.max_fn_params_bools), - None, - "consider refactoring bools into two-variant enums", - ); - } - } +fn has_n_bools<'tcx>(iter: impl Iterator<Item = &'tcx Ty<'tcx>>, mut count: u64) -> bool { + iter.filter(|ty| is_bool(ty)).any(|_| { + let (x, overflow) = count.overflowing_sub(1); + count = x; + overflow + }) } -impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); +fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) { + if has_n_bools(decl.inputs.iter(), max) && !sp.from_expansion() { + span_lint_and_help( + cx, + FN_PARAMS_EXCESSIVE_BOOLS, + sp, + format!("more than {max} bools in function parameters"), + None, + "consider refactoring bools into two-variant enums", + ); + } +} impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if item.span.from_expansion() { - return; - } - if let ItemKind::Struct(variant_data, _) = &item.kind { - if has_repr_attr(cx, item.hir_id()) { - return; - } - - if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } + if let ItemKind::Struct(variant_data, _) = &item.kind + && variant_data.fields().len() as u64 > self.max_struct_bools + && has_n_bools( + variant_data.fields().iter().map(|field| field.ty), + self.max_struct_bools, + ) + && !has_repr_attr(cx, item.hir_id()) + && !item.span.from_expansion() + { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); } } @@ -157,8 +146,9 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { // functions with a body are already checked by `check_fn` if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind && fn_sig.header.abi == Abi::Rust + && fn_sig.decl.inputs.len() as u64 > self.max_fn_params_bools { - self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); + check_fn_decl(cx, fn_sig.decl, fn_sig.span, self.max_fn_params_bools); } } @@ -171,12 +161,13 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { span: Span, def_id: LocalDefId, ) { - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && get_parent_as_impl(cx.tcx, hir_id).map_or(true, |impl_item| impl_item.of_trait.is_none()) + && fn_decl.inputs.len() as u64 > self.max_fn_params_bools + && get_parent_as_impl(cx.tcx, cx.tcx.local_def_id_to_hir_id(def_id)) + .map_or(true, |impl_item| impl_item.of_trait.is_none()) { - self.check_fn_sig(cx, fn_decl, span); + check_fn_decl(cx, fn_decl, span, self.max_fn_params_bools); } } } diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs index 4b0d11c5d1b..5154edd4399 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use rustc_ast::node_id::NodeSet; @@ -63,13 +64,19 @@ declare_clippy_lint! { } impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]); -#[derive(Clone)] pub struct ExcessiveNesting { pub excessive_nesting_threshold: u64, pub nodes: NodeSet, } impl ExcessiveNesting { + pub fn new(conf: &'static Conf) -> Self { + Self { + excessive_nesting_threshold: conf.excessive_nesting_threshold, + nodes: NodeSet::default(), + } + } + pub fn check_node_id(&self, cx: &EarlyContext<'_>, span: Span, node_id: NodeId) { if self.nodes.contains(&node_id) { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 436dc8611bd..0f4176ec73b 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -70,20 +70,24 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind - && cx.effective_visibilities.is_exported(item.owner_id.def_id) + let (lint, msg, fields) = match item.kind { + ItemKind::Enum(..) => ( + EXHAUSTIVE_ENUMS, + "exported enums should not be exhaustive", + [].as_slice(), + ), + ItemKind::Struct(v, ..) => ( + EXHAUSTIVE_STRUCTS, + "exported structs should not be exhaustive", + v.fields(), + ), + _ => return, + }; + if cx.effective_visibilities.is_exported(item.owner_id.def_id) && let attrs = cx.tcx.hir().attrs(item.hir_id()) && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) { - // skip structs with private fields - return; - } - (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") - } else { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - }; let suggestion_span = item.span.shrink_to_lo(); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 91c94d66458..f37d11f7eb9 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -47,8 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) - && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id - && let Node::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_node_by_def_id(parent) + && let parent = cx.tcx.hir().get_parent_item(e.hir_id) + && let OwnerNode::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_owner_node(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning && !is_entrypoint_fn(cx, parent.to_def_id()) 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 7484f772e08..bfe4e253ae4 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 @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -45,26 +46,11 @@ pub struct ExtraUnusedTypeParameters { } impl ExtraUnusedTypeParameters { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } - - /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items - /// if the `avoid_breaking_exported_api` config option is set. - fn is_empty_exported_or_macro( - &self, - cx: &LateContext<'_>, - span: Span, - def_id: LocalDefId, - body_id: BodyId, - ) -> bool { - let body = cx.tcx.hir().body(body_id).value; - let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none()); - let is_exported = cx.effective_visibilities.is_exported(def_id); - in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api) - } } impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); @@ -266,10 +252,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { } } +fn is_empty_body(cx: &LateContext<'_>, body: BodyId) -> bool { + matches!(cx.tcx.hir().body(body).value.kind, ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none()) +} + impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Fn(_, generics, body_id) = item.kind - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !generics.params.is_empty() + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, generics); @@ -281,8 +274,12 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind + && !item.generics.params.is_empty() && trait_ref_of_method(cx, item.owner_id.def_id).is_none() - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) + && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, item.generics); walk_impl_item(&mut walker, item); diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 2261fcdbdab..6adcd2235dc 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -62,10 +62,9 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Float(fty) = *ty.kind() - && let hir::ExprKind::Lit(lit) = expr.kind + if let hir::ExprKind::Lit(lit) = expr.kind && let LitKind::Float(sym, lit_float_ty) = lit.node + && let ty::Float(fty) = *cx.typeck_results().expr_ty(expr).kind() { let sym_str = sym.as_str(); let formatter = FloatFormat::new(sym_str); diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 99def199af0..a31d5cb6ec7 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -1,5 +1,6 @@ use arrayvec::ArrayVec; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ @@ -175,12 +176,11 @@ pub struct FormatArgs { } impl FormatArgs { - #[must_use] - pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - msrv, - ignore_mixed: allow_mixed_uninlined_format_args, + msrv: conf.msrv.clone(), + ignore_mixed: conf.allow_mixed_uninlined_format_args, } } } diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 09be7237b5c..e6f27cb82d1 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -97,7 +97,6 @@ struct FormatTraitNames { formatter_name: Option<Symbol>, } -#[derive(Default)] pub struct FormatImpl { format_args: FormatArgsStorage, // Whether we are inside Display or Debug trait impl - None for neither diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 1933a00891b..1c1d8b57bc4 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::path_def_id; @@ -54,9 +55,10 @@ pub struct FromOverInto { } impl FromOverInto { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - FromOverInto { msrv } + pub fn new(conf: &'static Conf) -> Self { + FromOverInto { + msrv: conf.msrv.clone(), + } } } @@ -64,10 +66,6 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { - return; - } - if let ItemKind::Impl(Impl { of_trait: Some(hir_trait_ref), self_ty, @@ -77,6 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into<target_ty> for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) + && span_is_local(item.span) && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) .map(ty::EarlyBinder::instantiate_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 82ce501bac5..9acb72b2e37 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -47,9 +47,13 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind - // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + + // check if the second argument is a primitive `10` + && is_integer_literal(radix, 10) + + // check if the second part of the path indeed calls the associated + // function `from_str_radix` + && pathseg.ident.name.as_str() == "from_str_radix" // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind @@ -57,12 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && let def::Res::PrimTy(prim_ty) = ty_res && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) - // check if the second part of the path indeed calls the associated - // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" - - // check if the second argument is a primitive `10` - && is_integer_literal(radix, 10) + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 26534492ddd..0f48941783b 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -7,10 +7,12 @@ mod result; mod too_many_arguments; mod too_many_lines; +use clippy_config::Conf; use clippy_utils::def_path_def_ids; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; @@ -391,39 +393,34 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.74.0"] + #[clippy::version = "1.80.0"] pub RENAMED_FUNCTION_PARAMS, restriction, "renamed function parameters in trait implementation" } -#[derive(Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, - allow_renamed_params_for: Vec<String>, /// A set of resolved `def_id` of traits that are configured to allow /// function params renaming. trait_ids: DefIdSet, } impl Functions { - pub fn new( - too_many_arguments_threshold: u64, - too_many_lines_threshold: u64, - large_error_threshold: u64, - avoid_breaking_exported_api: bool, - allow_renamed_params_for: Vec<String>, - ) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - too_many_arguments_threshold, - too_many_lines_threshold, - large_error_threshold, - avoid_breaking_exported_api, - allow_renamed_params_for, - trait_ids: DefIdSet::default(), + too_many_arguments_threshold: conf.too_many_arguments_threshold, + too_many_lines_threshold: conf.too_many_lines_threshold, + large_error_threshold: conf.large_error_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + trait_ids: conf + .allow_renamed_params_for + .iter() + .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>())) + .collect(), } } } @@ -479,12 +476,4 @@ impl<'tcx> LateLintPass<'tcx> for Functions { result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } - - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for path in &self.allow_renamed_params_for { - let path_segments: Vec<&str> = path.split("::").collect(); - let ids = def_path_def_ids(cx, &path_segments); - self.trait_ids.extend(ids); - } - } } diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index e6506709774..9488ba75686 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -66,15 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner()); if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_super_predicates(def_id); - let mut is_future = false; - for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) { - if let Some(trait_pred) = p.as_trait_clause() { - if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { - is_future = true; - break; - } - } - } + let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { + p.as_trait_clause().is_some_and(|trait_pred| { + Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() + }) + }); if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index a55836a972f..b38cc7b36a1 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{higher, SpanlessEq}; +use core::ops::ControlFlow; use rustc_errors::Diag; -use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -44,8 +45,6 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); impl<'tcx> LateLintPass<'tcx> for IfLetMutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let mut arm_visit = ArmVisitor { found_mutex: None, cx }; - let mut op_visit = OppVisitor { found_mutex: None, cx }; if let Some(higher::IfLet { let_expr, if_then, @@ -53,12 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { .. }) = higher::IfLet::hir(cx, expr) { - op_visit.visit_expr(let_expr); - if let Some(op_mutex) = op_visit.found_mutex { - arm_visit.visit_expr(if_then); - arm_visit.visit_expr(if_else); + let is_mutex_lock = |e: &'tcx Expr<'tcx>| { + if let Some(mutex) = is_mutex_lock_call(cx, e) { + ControlFlow::Break(mutex) + } else { + ControlFlow::Continue(()) + } + }; - if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) { + let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); + if let Some(op_mutex) = op_mutex { + let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); + if let Some(arm_mutex) = arm_mutex + && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) + { let diag = |diag: &mut Diag<'_, ()>| { diag.span_label( op_mutex.span, @@ -83,48 +90,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { } } -/// Checks if `Mutex::lock` is called in the `if let` expr. -pub struct OppVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -/// Checks if `Mutex::lock` is called in any of the branches. -pub struct ArmVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> { - self.found_mutex.and_then(|arm_mutex| { - SpanlessEq::new(self.cx) - .eq_expr(op_mutex, arm_mutex) - .then_some(arm_mutex) - }) - } -} - fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index 4dc1ff83771..2f6daeeb90d 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -56,44 +56,33 @@ fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { } impl LateLintPass<'_> for IfNotElse { - fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - // While loops will be desugared to ExprKind::If. This will cause the lint to fire. - // To fix this, return early if this span comes from a macro or desugaring. - if item.span.from_expansion() { - return; - } - if let ExprKind::If(cond, _, Some(els)) = item.kind { - if let ExprKind::Block(..) = els.kind { - // Disable firing the lint in "else if" expressions. - if is_else_clause(cx.tcx, item) { - return; - } + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::If(cond, _, Some(els)) = e.kind + && let ExprKind::DropTemps(cond) = cond.kind + && let ExprKind::Block(..) = els.kind + { + let (msg, help) = match cond.kind { + ExprKind::Unary(UnOp::Not, _) => ( + "unnecessary boolean `not` operation", + "remove the `!` and swap the blocks of the `if`/`else`", + ), + // Don't lint on `… != 0`, as these are likely to be bit tests. + // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. + ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ), + _ => return, + }; - match cond.peel_drop_temps().kind { - ExprKind::Unary(UnOp::Not, _) => { - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary boolean `not` operation", - None, - "remove the `!` and swap the blocks of the `if`/`else`", - ); - }, - ExprKind::Binary(ref kind, _, lhs) if kind.node == BinOpKind::Ne && !is_zero_const(lhs, cx) => { - // Disable firing the lint on `… != 0`, as these are likely to be bit tests. - // For example, `if foo & 0x0F00 != 0 { … } else { … }` already is in the "proper" order. - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary `!=` operation", - None, - "change to `==` and swap the blocks of the `if`/`else`", - ); - }, - _ => (), - } + // `from_expansion` will also catch `while` loops which appear in the HIR as: + // ```rust + // loop { + // if cond { ... } else { break; } + // } + // ``` + if !e.span.from_expansion() && !is_else_clause(cx.tcx, e) { + span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help); } } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 0b200815219..39ea16b05d1 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; @@ -51,9 +52,10 @@ pub struct IfThenSomeElseNone { } impl IfThenSomeElseNone { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -61,26 +63,6 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !self.msrv.meets(msrvs::BOOL_THEN) { - return; - } - - if in_external_macro(cx.sess(), expr.span) { - return; - } - - // We only care about the top-most `if` in the chain - if is_else_clause(cx.tcx, expr) { - return; - } - - // `bool::then()` and `bool::then_some()` are not const - if in_constant(cx, expr.hir_id) { - return; - } - - let ctxt = expr.span.ctxt(); - if let Some(higher::If { cond, then, @@ -89,9 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && let ctxt = expr.span.ctxt() && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && !is_else_clause(cx.tcx, expr) + && !in_constant(cx, expr.hir_id) + && !in_external_macro(cx.sess(), expr.span) + && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index a32201d8079..54b8adbc8ac 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -37,22 +37,21 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - if pat.span.from_expansion() { - return; - } - - match cx.tcx.parent_hir_node(pat.hir_id) { - Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { - // Ignore function parameters - return; - }, - Node::LetStmt(local) if local.ty.is_some() => { - // Ignore let bindings with explicit type - return; - }, - _ => {}, - } - if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() { + if matches!(pat.kind, PatKind::Wild) + && !pat.span.from_expansion() + && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() + { + match cx.tcx.parent_hir_node(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::LetStmt(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } span_lint_and_sugg( cx, IGNORED_UNIT_PATTERNS, diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 5c63d48adaf..12ca6d43b27 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_attr::{StabilityLevel, StableSince}; @@ -47,9 +48,9 @@ pub struct IncompatibleMsrv { impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl IncompatibleMsrv { - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), is_above_msrv: FxHashMap::default(), } } diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 1075975f0a2..5b0aadf35c6 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !expr.span.from_expansion() - && let ExprKind::Struct(qpath, fields, base) = expr.kind + if let ExprKind::Struct(qpath, fields, base) = expr.kind + && fields.iter().all(|f| f.is_shorthand) + && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() && let Some(variant) = adt_def.variants().iter().next() - && fields.iter().all(|f| f.is_shorthand) { let mut def_order_map = FxHashMap::default(); for (idx, field) in variant.fields.iter().enumerate() { 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 128461ce7bc..526b4e1fba0 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; @@ -58,10 +59,10 @@ pub struct IndexRefutableSlice { } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - max_suggested_slice: max_suggested_slice_pattern_length, - msrv, + max_suggested_slice: conf.max_suggested_slice_pattern_length, + msrv: conf.msrv.clone(), } } } @@ -70,8 +71,8 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) - && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && self.msrv.meets(msrvs::SLICE_PATTERNS) && let found_slices = find_slice_values(cx, let_pat) diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index d54f2af65cd..6729c7c8d10 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -1,5 +1,6 @@ //! lint on indexing and slicing operations +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; @@ -87,28 +88,22 @@ declare_clippy_lint! { impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); -#[derive(Copy, Clone)] pub struct IndexingSlicing { suppress_restriction_lint_in_const: bool, } impl IndexingSlicing { - pub fn new(suppress_restriction_lint_in_const: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - suppress_restriction_lint_in_const, + suppress_restriction_lint_in_const: conf.suppress_restriction_lint_in_const, } } } impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id)) - || is_from_proc_macro(cx, expr) - { - return; - } - if let ExprKind::Index(array, index, _) = &expr.kind + && (!self.suppress_restriction_lint_in_const || !cx.tcx.hir().is_inside_const_context(expr.hir_id)) && let expr_ty = cx.typeck_results().expr_ty(array) && let mut deref = deref_chain(cx, expr_ty) && deref.any(|l| { @@ -116,6 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { || l.peel_refs().is_array() || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr) }) + && !is_from_proc_macro(cx, expr) { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 9ad02735878..fa7e7f6b76d 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -226,13 +226,14 @@ const INFINITE_COLLECTORS: &[Symbol] = &[ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { + let method_str = method.ident.name.as_str(); for &(name, len) in &COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return is_infinite(cx, receiver); } } for &(name, len) in &POSSIBLY_COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return MaybeInfinite.and(is_infinite(cx, receiver)); } } diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 5fe152d1e30..f41fdf3203c 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; @@ -68,9 +69,10 @@ pub struct InstantSubtraction { } impl InstantSubtraction { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 33764d3eb09..4d44bae02b8 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -1,5 +1,6 @@ //! lint on enum variants that are prefixed or suffixed by the same characters +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::is_bool; use clippy_utils::macros::span_is_local; @@ -152,21 +153,14 @@ pub struct ItemNameRepetitions { } impl ItemNameRepetitions { - #[must_use] - pub fn new( - enum_threshold: u64, - struct_threshold: u64, - avoid_breaking_exported_api: bool, - allow_private_module_inception: bool, - allowed_prefixes: &[String], - ) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { modules: Vec::new(), - enum_threshold, - struct_threshold, - avoid_breaking_exported_api, - allow_private_module_inception, - allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), + enum_threshold: conf.enum_variant_name_threshold, + struct_threshold: conf.struct_field_name_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_private_module_inception: conf.allow_private_module_inception, + allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } } diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index b18ab625e60..5d2b521b250 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -32,13 +33,14 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u128, + maximum_allowed_size: u64, } impl LargeConstArrays { - #[must_use] - pub fn new(maximum_allowed_size: u128) -> Self { - Self { maximum_allowed_size } + pub fn new(conf: &'static Conf) -> Self { + Self { + maximum_allowed_size: conf.array_size_threshold, + } } } @@ -57,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) - && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); let sugg_span = Span::new( diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index 85daadcc537..225d79aa71d 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -1,5 +1,6 @@ //! lint when there is a large size difference between variants on an enum +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo}; @@ -59,16 +60,14 @@ declare_clippy_lint! { "large size difference between variants on an enum" } -#[derive(Copy, Clone)] pub struct LargeEnumVariant { maximum_size_difference_allowed: u64, } impl LargeEnumVariant { - #[must_use] - pub fn new(maximum_size_difference_allowed: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_size_difference_allowed, + maximum_size_difference_allowed: conf.enum_variant_size_threshold, } } } diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index 602227e4249..6f5065e4936 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; @@ -39,14 +40,15 @@ declare_clippy_lint! { "large future may lead to unexpected stack overflows" } -#[derive(Copy, Clone)] pub struct LargeFuture { future_size_threshold: u64, } impl LargeFuture { - pub fn new(future_size_threshold: u64) -> Self { - Self { future_size_threshold } + pub fn new(conf: &'static Conf) -> Self { + Self { + future_size_threshold: conf.future_size_threshold, + } } } diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 2688283a6ce..c67da689aae 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; @@ -41,9 +42,10 @@ pub struct LargeIncludeFile { } impl LargeIncludeFile { - #[must_use] - pub fn new(max_file_size: u64) -> Self { - Self { max_file_size } + pub fn new(conf: &'static Conf) -> Self { + Self { + max_file_size: conf.max_include_file_size, + } } } diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index d94b0cce948..15a75b06089 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::macros::macro_backtrace; @@ -27,15 +28,14 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u128, + maximum_allowed_size: u64, prev_vec_macro_callsite: Option<Span>, } impl LargeStackArrays { - #[must_use] - pub fn new(maximum_allowed_size: u128) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_allowed_size, + maximum_allowed_size: conf.array_size_threshold, prev_vec_macro_callsite: None, } } @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { }) ) }) - && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { span_lint_and_then( cx, @@ -106,7 +106,7 @@ fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { /// /// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the /// correct result. - fn repeat_expr_might_be_expanded<'tcx>(expr: &Expr<'tcx>) -> bool { + fn repeat_expr_might_be_expanded(expr: &Expr<'_>) -> bool { let ExprKind::Repeat(_, ArrayLen::Body(len_ct)) = expr.kind else { return false; }; diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index 49408d7e243..4abf7edc9b4 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -1,5 +1,6 @@ use std::{fmt, ops}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::source::snippet_opt; @@ -85,10 +86,9 @@ pub struct LargeStackFrames { } impl LargeStackFrames { - #[must_use] - pub fn new(size: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_allowed_size: size, + maximum_allowed_size: conf.stack_size_threshold, } } } diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index a08b40bef37..752e1326e3e 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::{get_parent_expr, is_from_proc_macro}; use hir::def_id::DefId; @@ -38,9 +39,10 @@ pub struct LegacyNumericConstants { } impl LegacyNumericConstants { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c2dc26d6605..917d9d36076 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,6 +1,7 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] @@ -288,6 +289,7 @@ mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; +mod pathbuf_init_then_push; mod pattern_type_mismatch; mod permissions_set_readonly_false; mod precedence; @@ -394,7 +396,6 @@ use clippy_config::{get_configuration_metadata, Conf}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use std::collections::BTreeMap; /// Register all pre expansion lints /// @@ -406,9 +407,7 @@ use std::collections::BTreeMap; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - let msrv = || conf.msrv.clone(); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); } #[derive(Default)] @@ -534,88 +533,6 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - let Conf { - ref absolute_paths_allowed_crates, - absolute_paths_max_segments, - accept_comment_above_attributes, - accept_comment_above_statement, - allow_dbg_in_tests, - allow_expect_in_tests, - allow_mixed_uninlined_format_args, - allow_one_hash_in_raw_strings, - allow_panic_in_tests, - allow_print_in_tests, - allow_private_module_inception, - allow_unwrap_in_tests, - allow_useless_vec_in_tests, - ref allowed_dotfiles, - ref allowed_idents_below_min_chars, - ref allowed_scripts, - ref allowed_wildcard_imports, - ref arithmetic_side_effects_allowed_binary, - ref arithmetic_side_effects_allowed_unary, - ref arithmetic_side_effects_allowed, - array_size_threshold, - avoid_breaking_exported_api, - ref await_holding_invalid_types, - cargo_ignore_publish, - cognitive_complexity_threshold, - ref disallowed_macros, - ref disallowed_methods, - ref disallowed_names, - ref disallowed_types, - ref doc_valid_idents, - enable_raw_pointer_heuristic_for_send, - enforce_iter_loop_reborrow, - ref enforced_import_renames, - enum_variant_name_threshold, - enum_variant_size_threshold, - excessive_nesting_threshold, - future_size_threshold, - ref ignore_interior_mutability, - large_error_threshold, - literal_representation_threshold, - matches_for_let_else, - max_fn_params_bools, - max_include_file_size, - max_struct_bools, - max_suggested_slice_pattern_length, - max_trait_bounds, - min_ident_chars_threshold, - missing_docs_in_crate_items, - ref msrv, - pass_by_value_size_limit, - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, - single_char_binding_names_threshold, - stack_size_threshold, - ref standard_macro_braces, - struct_field_name_threshold, - suppress_restriction_lint_in_const, - too_large_for_stack, - too_many_arguments_threshold, - too_many_lines_threshold, - trivial_copy_size_limit, - type_complexity_threshold, - unnecessary_box_size, - unreadable_literal_lint_fractions, - upper_case_acronyms_aggressive, - vec_box_size_threshold, - verbose_bit_mask_threshold, - warn_on_all_wildcard_imports, - check_private_items, - pub_underscore_fields_behavior, - ref allowed_duplicate_crates, - allow_comparison_to_zero, - ref allowed_prefixes, - ref allow_renamed_params_for, - - blacklisted_names: _, - cyclomatic_complexity_threshold: _, - warn_unsafe_macro_metavars_in_private_macros, - } = *conf; - let msrv = || msrv.clone(); - register_removed_non_tool_lints(store); register_categories(store); @@ -660,35 +577,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); } - store.register_late_pass(move |_| { - Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( - arithmetic_side_effects_allowed - .iter() - .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]]) - .chain(arithmetic_side_effects_allowed_binary.clone()) - .collect(), - arithmetic_side_effects_allowed - .iter() - .chain(arithmetic_side_effects_allowed_unary.iter()) - .cloned() - .collect(), - )) - }); + store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); - store.register_late_pass(move |_| { - Box::new(await_holding_invalid::AwaitHolding::new( - await_holding_invalid_types.clone(), - )) - }); + store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))); store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); - store.register_late_pass(move |_| { - Box::new(types::Types::new( - vec_box_size_threshold, - type_complexity_threshold, - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(types::Types::new(conf))); store.register_late_pass(|_| Box::new(booleans::NonminimalBool)); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); @@ -702,7 +596,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); - store.register_late_pass(move |_| Box::new(attrs::Attributes::new(msrv()))); + 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)); store.register_late_pass(|_| Box::new(uninit_vec::UninitVec)); @@ -714,44 +608,30 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| { - Box::new(methods::Methods::new( - avoid_breaking_exported_api, - msrv(), - allow_expect_in_tests, - allow_unwrap_in_tests, - allowed_dotfiles.clone(), - format_args.clone(), - )) - }); - store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv()))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv()))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv()))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); + store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(conf))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(conf))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(conf))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(conf))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(conf))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); - store.register_late_pass(move |_| { - Box::new(index_refutable_slice::IndexRefutableSlice::new( - max_suggested_slice_pattern_length, - msrv(), - )) - }); + store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))); store.register_late_pass(|_| Box::<shadow::Shadow>::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); - store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); + store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); @@ -763,75 +643,49 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::<no_effect::NoEffect>::default()); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); - store.register_late_pass(move |_| { - Box::new(cognitive_complexity::CognitiveComplexity::new( - cognitive_complexity_threshold, - )) - }); - store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack })); - store.register_late_pass(move |_| { - Box::new(vec::UselessVec { - too_large_for_stack, - msrv: msrv(), - span_to_lint_map: BTreeMap::new(), - allow_in_test: allow_useless_vec_in_tests, - }) - }); - store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests })); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(conf))); + store.register_late_pass(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))); + store.register_late_pass(move |_| Box::new(escape::BoxedLocal::new(conf))); + store.register_late_pass(move |_| Box::new(vec::UselessVec::new(conf))); + store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); + 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::<regex::Regex>::default()); - store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)); store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default()); - store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names))); - store.register_late_pass(move |_| { - Box::new(functions::Functions::new( - too_many_arguments_threshold, - too_many_lines_threshold, - large_error_threshold, - avoid_breaking_exported_api, - allow_renamed_params_for.clone(), - )) - }); - store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items))); + store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))); + store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf))); + store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf))); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); - store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items))); + store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); - store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); + store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); - store.register_late_pass(move |tcx| { - Box::new(pass_by_ref_or_value::PassByRefOrValue::new( - trivial_copy_size_limit, - pass_by_value_size_limit, - avoid_breaking_exported_api, - tcx.sess.target.pointer_width, - )) - }); + store.register_late_pass(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))); store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); - store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(msrv(), matches_for_let_else))); + store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(conf))); store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed)); store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); @@ -839,22 +693,18 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(move |_| { - Box::new(indexing_slicing::IndexingSlicing::new( - suppress_restriction_lint_in_const, - )) - }); - store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); + store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); - store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); + store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); - store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv()))); + store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); let format_args = format_args_storage.clone(); @@ -875,80 +725,45 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); - store.register_early_pass(move || { - Box::new(literal_representation::LiteralDigitGrouping::new( - unreadable_literal_lint_fractions, - )) - }); - store.register_early_pass(move || { - Box::new(literal_representation::DecimalLiteralRepresentation::new( - literal_representation_threshold, - )) - }); - store.register_late_pass(move |_| { - Box::new(item_name_repetitions::ItemNameRepetitions::new( - enum_variant_name_threshold, - struct_field_name_threshold, - avoid_breaking_exported_api, - allow_private_module_inception, - allowed_prefixes, - )) - }); + store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))); + store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))); + store.register_late_pass(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))); store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); - store.register_late_pass(move |_| { - Box::new(upper_case_acronyms::UpperCaseAcronyms::new( - avoid_breaking_exported_api, - upper_case_acronyms_aggressive, - )) - }); + store.register_late_pass(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))); store.register_late_pass(|_| Box::<default::Default>::default()); - store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); + store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); - store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold.into()))); - store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold.into()))); + store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); + store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_late_pass(|_| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()); - store.register_late_pass(move |_| { - Box::new(excessive_bools::ExcessiveBools::new( - max_struct_bools, - max_fn_params_bools, - )) - }); + store.register_late_pass(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); - store.register_late_pass(move |_| { - Box::new(wildcard_imports::WildcardImports::new( - warn_on_all_wildcard_imports, - allowed_wildcard_imports.clone(), - )) - }); + store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); - store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold))); + store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(conf))); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); store.register_late_pass(|_| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn)); - store.register_early_pass(move || { - Box::new(non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }) - }); - store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(standard_macro_braces))); + store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))); + store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); - store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); + store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop)); @@ -958,86 +773,57 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default()); - store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); - store.register_late_pass(move |_| { - Box::new(missing_enforced_import_rename::ImportRename::new( - enforced_import_renames.clone(), - )) - }); - store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(allowed_scripts))); + store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); + store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))); store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); - store.register_late_pass(move |_| { - Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new( - enable_raw_pointer_heuristic_for_send, - )) - }); - store.register_late_pass(move |_| { - Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new( - accept_comment_above_statement, - accept_comment_above_attributes, - )) - }); + store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))); + store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| { - Box::new(format_args::FormatArgs::new( - format_args.clone(), - msrv(), - allow_mixed_uninlined_format_args, - )) - }); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(conf, format_args.clone()))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(conf))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); - store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); + store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests))); - store.register_late_pass(move |_| { - Box::new(cargo::Cargo { - ignore_publish: cargo_ignore_publish, - allowed_duplicate_crates: allowed_duplicate_crates.clone(), - }) - }); + store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); + store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); - store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv()))); + store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); - store.register_late_pass(move |_| { - Box::new(operators::Operators::new( - verbose_bit_mask_threshold, - allow_comparison_to_zero, - )) - }); + store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); @@ -1048,44 +834,25 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); - store.register_late_pass(move |_| { - Box::new(semicolon_block::SemicolonBlock::new( - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, - )) - }); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))); + store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))); store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); - store.register_late_pass(move |_| { - Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new( - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))); store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); - store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); + 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( - avoid_breaking_exported_api, - unnecessary_box_size, - )) - }); + store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk)); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); - store.register_early_pass(move || { - Box::new(excessive_nesting::ExcessiveNesting { - excessive_nesting_threshold, - nodes: rustc_ast::node_id::NodeSet::new(), - }) - }); + store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); @@ -1095,44 +862,21 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { 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(move |_| { - Box::new(min_ident_chars::MinIdentChars { - allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(), - min_ident_chars_threshold, - }) - }); - store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold))); + 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)); - store.register_late_pass(move |_| { - Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new( - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); - store.register_late_pass(move |_| { - Box::new(single_call_fn::SingleCallFn { - avoid_breaking_exported_api, - def_id_to_usage: rustc_data_structures::fx::FxIndexMap::default(), - }) - }); - store.register_early_pass(move || { - Box::new(raw_strings::RawStrings { - allow_one_hash_in_raw_strings, - }) - }); - store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(msrv()))); + store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); + store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); + store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); - store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); + store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))); store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); - store.register_late_pass(move |_| { - Box::new(absolute_paths::AbsolutePaths { - absolute_paths_max_segments, - absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(), - }) - }); + store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))); store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals)); store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default()); @@ -1141,38 +885,29 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); store.register_late_pass(move |_| { Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( - msrv(), + conf, )) }); - store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))); store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); + store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default()); store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity)); store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()); - store.register_late_pass(move |_| { - Box::new(pub_underscore_fields::PubUnderscoreFields { - behavior: pub_underscore_fields_behavior, - }) - }); - store - .register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(msrv()))); - store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); + store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))); + store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))); + store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(conf))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); - store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(msrv()))); + 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(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); - store.register_late_pass(move |_| { - Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { - warn_unsafe_macro_metavars_in_private_macros, - ..Default::default() - }) - }); - store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv()))); + 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)); store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 443d6189c1f..ba34af9c100 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -22,6 +22,7 @@ use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -380,11 +381,8 @@ fn could_use_elision<'tcx>( return None; } - let mut checker = BodyLifetimeChecker { - lifetimes_used_in_body: false, - }; - checker.visit_expr(body.value); - if checker.lifetimes_used_in_body { + let mut checker = BodyLifetimeChecker; + if checker.visit_expr(body.value).is_break() { return None; } } @@ -545,7 +543,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } - for bound in bounds { + for (bound, _) in bounds { self.visit_poly_trait_ref(bound); } }, @@ -694,15 +692,15 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' } } -struct BodyLifetimeChecker { - lifetimes_used_in_body: bool, -} +struct BodyLifetimeChecker; impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { + type Result = ControlFlow<()>; // for lifetimes as parameters of generics - fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> { if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { - self.lifetimes_used_in_body = true; + return ControlFlow::Break(()); } + ControlFlow::Continue(()) } } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 7481543941a..b685d1dad1a 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -1,6 +1,7 @@ //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; @@ -218,7 +219,6 @@ impl WarningType { } } -#[derive(Copy, Clone)] pub struct LiteralDigitGrouping { lint_fraction_readability: bool, } @@ -245,13 +245,13 @@ impl EarlyLintPass for LiteralDigitGrouping { const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; impl LiteralDigitGrouping { - pub fn new(lint_fraction_readability: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - lint_fraction_readability, + lint_fraction_readability: conf.unreadable_literal_lint_fractions, } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { + fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { if let Some(src) = snippet_opt(cx, span) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) @@ -437,7 +437,6 @@ impl LiteralDigitGrouping { } #[expect(clippy::module_name_repetitions)] -#[derive(Copy, Clone)] pub struct DecimalLiteralRepresentation { threshold: u64, } @@ -455,11 +454,12 @@ impl EarlyLintPass for DecimalLiteralRepresentation { } impl DecimalLiteralRepresentation { - #[must_use] - pub fn new(threshold: u64) -> Self { - Self { threshold } + pub fn new(conf: &'static Conf) -> Self { + Self { + threshold: conf.literal_representation_threshold, + } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { + fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 64ea591993d..7c2a8098af2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -23,6 +23,7 @@ mod while_let_loop; mod while_let_on_iterator; use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::higher; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; @@ -717,10 +718,10 @@ pub struct Loops { enforce_iter_loop_reborrow: bool, } impl Loops { - pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, - enforce_iter_loop_reborrow, + msrv: conf.msrv.clone(), + enforce_iter_loop_reborrow: conf.enforce_iter_loop_reborrow, } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 6c6a9a1a2e0..21f9a71f2c5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -8,6 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; use rustc_span::Span; +use std::ops::ControlFlow; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { if let Some(higher::Range { @@ -114,7 +115,6 @@ impl MutatePairDelegate<'_, '_> { struct BreakAfterExprVisitor { hir_id: HirId, past_expr: bool, - past_candidate: bool, break_after_expr: bool, } @@ -123,7 +123,6 @@ impl BreakAfterExprVisitor { let mut visitor = BreakAfterExprVisitor { hir_id, past_expr: false, - past_candidate: false, break_after_expr: false, }; @@ -135,21 +134,19 @@ impl BreakAfterExprVisitor { } impl<'tcx> Visitor<'tcx> for BreakAfterExprVisitor { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.past_candidate { - return; - } - + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { if expr.hir_id == self.hir_id { self.past_expr = true; + ControlFlow::Continue(()) } else if self.past_expr { if matches!(&expr.kind, ExprKind::Break(..)) { self.break_after_expr = true; } - self.past_candidate = true; + ControlFlow::Break(()) } else { - intravisit::walk_expr(self, expr); + intravisit::walk_expr(self, expr) } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 108fdb69775..2b41911e8ec 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::contains_break_or_continue; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; +use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition; use rustc_span::sym; @@ -70,7 +70,10 @@ pub(super) fn check<'tcx>( && !contains_break_or_continue(body) { let mut applicability = Applicability::MachineApplicable; - let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + let mut pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + if matches!(pat.kind, PatKind::Or(..)) { + pat_snip = format!("({pat_snip})").into(); + } let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); block_str.remove(0); diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs index 3dff826cb85..e7b3a2c4973 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Expr, ExprKind, HirIdSet, QPath}; use rustc_lint::LateContext; +use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { if constant(cx, cx.typeck_results(), cond).is_some() { @@ -35,11 +36,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' }; let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v); - let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { - has_break_or_return: false, - }; - has_break_or_return_visitor.visit_expr(expr); - let has_break_or_return = has_break_or_return_visitor.has_break_or_return; + let mut has_break_or_return_visitor = HasBreakOrReturnVisitor; + let has_break_or_return = has_break_or_return_visitor.visit_expr(expr).is_break(); if no_cond_variable_mutated && !mutable_static_in_cond { span_lint_and_then( @@ -59,25 +57,19 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' } } -struct HasBreakOrReturnVisitor { - has_break_or_return: bool, -} +struct HasBreakOrReturnVisitor; impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.has_break_or_return { - return; - } - + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> ControlFlow<()> { match expr.kind { ExprKind::Ret(_) | ExprKind::Break(_, _) => { - self.has_break_or_return = true; - return; + return ControlFlow::Break(()); }, _ => {}, } - walk_expr(self, expr); + walk_expr(self, expr) } } diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index d1ae243877d..fed58f7ff14 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -1,6 +1,4 @@ -use std::collections::btree_map::Entry; -use std::collections::BTreeMap; - +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; @@ -10,6 +8,8 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{sym, Span, SyntaxContext}; +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; declare_clippy_lint! { /// ### What it does @@ -90,9 +90,8 @@ pub enum MetavarState { ReferencedInSafe, } -#[derive(Default)] pub struct ExprMetavarsInUnsafe { - pub warn_unsafe_macro_metavars_in_private_macros: bool, + warn_unsafe_macro_metavars_in_private_macros: bool, /// A metavariable can be expanded more than once, potentially across multiple bodies, so it /// requires some state kept across HIR nodes to make it possible to delay a warning /// and later undo: @@ -106,7 +105,16 @@ pub struct ExprMetavarsInUnsafe { /// } /// } /// ``` - pub metavar_expns: BTreeMap<Span, MetavarState>, + metavar_expns: BTreeMap<Span, MetavarState>, +} + +impl ExprMetavarsInUnsafe { + pub fn new(conf: &'static Conf) -> Self { + Self { + warn_unsafe_macro_metavars_in_private_macros: conf.warn_unsafe_macro_metavars_in_private_macros, + metavar_expns: BTreeMap::new(), + } + } } struct BodyVisitor<'a, 'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index d9f6be6dc2b..5cbdc7f864a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -34,15 +35,15 @@ declare_clippy_lint! { "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`" } -#[derive(Clone)] pub struct ManualBits { msrv: Msrv, } impl ManualBits { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index e2ab4415518..a79ad018a04 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; @@ -97,8 +98,10 @@ pub struct ManualClamp { } impl ManualClamp { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index daa8101aa5f..1c568b1b74f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{is_local_used, local_used_once}; @@ -51,9 +52,10 @@ pub struct ManualHashOne { } impl ManualHashOne { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 6f6ba1852a6..a9f21d34e4c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; @@ -62,9 +63,10 @@ pub struct ManualIsAsciiCheck { } impl ManualIsAsciiCheck { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs index 8e8cdc3fb07..db491a8c8f6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs +++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs}; use rustc_errors::Applicability; @@ -37,9 +38,10 @@ pub struct ManualMainSeparatorStr { } impl ManualMainSeparatorStr { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 73a505fd73f..28cfe22835a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; use clippy_utils::source::snippet_opt; @@ -67,9 +68,10 @@ pub struct ManualNonExhaustiveStruct { } impl ManualNonExhaustiveStruct { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -83,10 +85,9 @@ pub struct ManualNonExhaustiveEnum { } impl ManualNonExhaustiveEnum { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), constructed_enum_variants: FxHashSet::default(), potential_enums: Vec::new(), } diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index b518dc26937..78a750f0dcd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; @@ -38,9 +39,10 @@ pub struct ManualRemEuclid { } impl ManualRemEuclid { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 8f7b8abd0c3..09f6362b4dd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -59,9 +60,10 @@ pub struct ManualRetain { } impl ManualRetain { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 6a523ad1564..686ecccf829 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; @@ -50,9 +51,10 @@ pub struct ManualStrip { } impl ManualStrip { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } 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 0940fc3219b..85a08f81c2f 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 @@ -35,6 +35,17 @@ pub(super) fn check_if_let<'tcx>( else_expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(let_expr); + let then_ty = cx.typeck_results().expr_ty(then_expr); + // The signature is `fn unwrap_or<T>(self: Option<T>, default: T) -> T`. + // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. + // Otherwise, type error will occur. + if cx.typeck_results().expr_adjustments(then_expr).is_empty() + && let rustc_middle::ty::Adt(_did, args) = ty.kind() + && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) + && some_ty != then_ty + { + return; + } check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 22a299ae3d8..cec3504ed8d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -25,6 +25,7 @@ mod try_err; mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::source::walk_span_to_context; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; @@ -980,10 +981,9 @@ pub struct Matches { } impl Matches { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), infallible_destructuring_match_linted: false, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 9047c9627d9..9c2f42d2187 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -232,7 +232,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { enum SigDropHolder { /// No values with significant drop present in this expression. /// - /// Expressions that we've emited lints do not count. + /// Expressions that we've emitted lints do not count. None, /// Some field in this expression references to values with significant drop. /// @@ -426,7 +426,7 @@ fn ty_has_erased_regions(ty: Ty<'_>) -> bool { impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - // We've emited a lint on some neighborhood expression. That lint will suggest to move out the + // We've emitted a lint on some neighborhood expression. That lint will suggest to move out the // _parent_ expression (not the expression itself). Since we decide to move out the parent // expression, it is pointless to continue to process the current expression. if self.sig_drop_holder == SigDropHolder::Moved { @@ -450,7 +450,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == ex.hir_id && self.sig_drop_holder == SigDropHolder::Moved => { - // Never move out only the assignee. Instead, we should always move out the whole assigment. + // Never move out only the assignee. Instead, we should always move out the whole assignment. self.replace_current_sig_drop(parent_ex.span, true, 0); }, _ => { diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 578aa7989e7..72a5945ad9b 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; @@ -217,9 +218,10 @@ pub struct MemReplace { } impl MemReplace { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index fb440ce656e..20f3722e173 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -10,59 +10,80 @@ use rustc_hir::{LangItem, QPath}; use rustc_lint::LateContext; use rustc_span::Span; -pub(crate) struct OptionAndThenSome; - -impl BindInsteadOfMap for OptionAndThenSome { - const VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome; - const BAD_METHOD_NAME: &'static str = "and_then"; - const GOOD_METHOD_NAME: &'static str = "map"; +pub(super) fn check_and_then_some( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::OptionSome, + bad_method_name: "and_then", + good_method_name: "map", + } + .check(cx, expr, recv, arg) } -pub(crate) struct ResultAndThenOk; - -impl BindInsteadOfMap for ResultAndThenOk { - const VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk; - const BAD_METHOD_NAME: &'static str = "and_then"; - const GOOD_METHOD_NAME: &'static str = "map"; +pub(super) fn check_and_then_ok( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::ResultOk, + bad_method_name: "and_then", + good_method_name: "map", + } + .check(cx, expr, recv, arg) } -pub(crate) struct ResultOrElseErrInfo; - -impl BindInsteadOfMap for ResultOrElseErrInfo { - const VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr; - const BAD_METHOD_NAME: &'static str = "or_else"; - const GOOD_METHOD_NAME: &'static str = "map_err"; +pub(super) fn check_or_else_err( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::ResultErr, + bad_method_name: "or_else", + good_method_name: "map_err", + } + .check(cx, expr, recv, arg) } -pub(crate) trait BindInsteadOfMap { - const VARIANT_LANG_ITEM: LangItem; - const BAD_METHOD_NAME: &'static str; - const GOOD_METHOD_NAME: &'static str; +struct BindInsteadOfMap { + variant_lang_item: LangItem, + bad_method_name: &'static str, + good_method_name: &'static str, +} - fn no_op_msg(cx: &LateContext<'_>) -> Option<String> { - let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; +impl BindInsteadOfMap { + fn no_op_msg(&self, cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().get(self.variant_lang_item)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}({})`, which is a no-op", cx.tcx.item_name(item_id), - Self::BAD_METHOD_NAME, + self.bad_method_name, cx.tcx.item_name(variant_id), )) } - fn lint_msg(cx: &LateContext<'_>) -> Option<String> { - let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; + fn lint_msg(&self, cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().get(self.variant_lang_item)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", cx.tcx.item_name(item_id), - Self::BAD_METHOD_NAME, + self.bad_method_name, cx.tcx.item_name(variant_id), - Self::GOOD_METHOD_NAME + self.good_method_name, )) } fn lint_closure_autofixable( + &self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, @@ -71,9 +92,9 @@ pub(crate) trait BindInsteadOfMap { ) -> bool { if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind && let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind - && Self::is_variant(cx, path.res) + && self.is_variant(cx, path.res) && !contains_return(inner_expr) - && let Some(msg) = Self::lint_msg(cx) + && let Some(msg) = self.lint_msg(cx) { let mut app = Applicability::MachineApplicable; let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; @@ -82,7 +103,7 @@ pub(crate) trait BindInsteadOfMap { let option_snip = snippet(cx, recv.span, ".."); let note = format!( "{option_snip}.{}({closure_args_snip} {some_inner_snip})", - Self::GOOD_METHOD_NAME + self.good_method_name ); span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, "try", note, app); true @@ -91,13 +112,13 @@ pub(crate) trait BindInsteadOfMap { } } - fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { + fn lint_closure(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if !ret_expr.span.from_expansion() && let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind && let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind - && Self::is_variant(cx, path.res) + && self.is_variant(cx, path.res) && !contains_return(arg) { suggs.push((ret_expr.span, arg.span.source_callsite())); @@ -108,7 +129,7 @@ pub(crate) trait BindInsteadOfMap { }); let (span, msg) = if can_sugg && let hir::ExprKind::MethodCall(segment, ..) = expr.kind - && let Some(msg) = Self::lint_msg(cx) + && let Some(msg) = self.lint_msg(cx) { (segment.ident.span, msg) } else { @@ -119,7 +140,7 @@ pub(crate) trait BindInsteadOfMap { diag, "try", Applicability::MachineApplicable, - std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain( + std::iter::once((span, self.good_method_name.into())).chain( suggs .into_iter() .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), @@ -130,9 +151,9 @@ pub(crate) trait BindInsteadOfMap { } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { + fn check(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) + && let Some(vid) = cx.tcx.lang_items().get(self.variant_lang_item) && adt.did() == cx.tcx.parent(vid) { } else { @@ -144,15 +165,15 @@ pub(crate) trait BindInsteadOfMap { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); - if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) { + if self.lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) { true } else { - Self::lint_closure(cx, expr, closure_expr) + self.lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => { - if let Some(msg) = Self::no_op_msg(cx) { + hir::ExprKind::Path(QPath::Resolved(_, path)) if self.is_variant(cx, path.res) => { + if let Some(msg) = self.no_op_msg(cx) { span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, @@ -169,9 +190,9 @@ pub(crate) trait BindInsteadOfMap { } } - fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { + fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool { if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) { + if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) { return cx.tcx.parent(id) == variant_id; } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index a846552cddf..aa4cad9342a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -131,8 +131,8 @@ mod waker_clone_wake; mod wrong_self_convention; mod zst_offset; -use bind_instead_of_map::BindInsteadOfMap; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; @@ -4131,27 +4131,20 @@ pub struct Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, - allowed_dotfiles: FxHashSet<String>, + allowed_dotfiles: FxHashSet<&'static str>, format_args: FormatArgsStorage, } impl Methods { - #[must_use] - pub fn new( - avoid_breaking_exported_api: bool, - msrv: Msrv, - allow_expect_in_tests: bool, - allow_unwrap_in_tests: bool, - mut allowed_dotfiles: FxHashSet<String>, - format_args: FormatArgsStorage, - ) -> Self { - allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { + let mut allowed_dotfiles: FxHashSet<_> = conf.allowed_dotfiles.iter().map(|s| &**s).collect(); + allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES); Self { - avoid_breaking_exported_api, - msrv, - allow_expect_in_tests, - allow_unwrap_in_tests, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + msrv: conf.msrv.clone(), + allow_expect_in_tests: conf.allow_expect_in_tests, + allow_unwrap_in_tests: conf.allow_unwrap_in_tests, allowed_dotfiles, format_args, } @@ -4512,8 +4505,8 @@ impl Methods { } }, ("and_then", [arg]) => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } @@ -4853,7 +4846,7 @@ impl Methods { open_options::check(cx, expr, recv); }, ("or_else", [arg]) => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 3d326bc99f9..ed3bed42eb3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -10,6 +10,7 @@ use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_span::{sym, Span}; +use std::ops::ControlFlow; use super::MAP_UNWRAP_OR; @@ -54,15 +55,14 @@ pub(super) fn check<'tcx>( let mut reference_visitor = ReferenceVisitor { cx, identifiers: unwrap_visitor.identifiers, - found_reference: false, unwrap_or_span: unwrap_arg.span, }; let map = cx.tcx.hir(); let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id)); - reference_visitor.visit_body(body); - if reference_visitor.found_reference { + // Visit the body, and return if we've found a reference + if reference_visitor.visit_body(body).is_break() { return; } } @@ -151,29 +151,27 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { struct ReferenceVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, identifiers: FxHashSet<HirId>, - found_reference: bool, unwrap_or_span: Span, } impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> { // If we haven't found a reference yet, check if this references // one of the locals that was moved in the `unwrap_or` argument. // We are only interested in exprs that appear before the `unwrap_or` call. - if !self.found_reference { - if expr.span < self.unwrap_or_span - && let ExprKind::Path(ref path) = expr.kind - && let QPath::Resolved(_, path) = path - && let Res::Local(local_id) = path.res - && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) - && let PatKind::Binding(_, local_id, ..) = pat.kind - && self.identifiers.contains(&local_id) - { - self.found_reference = true; - } - rustc_hir::intravisit::walk_expr(self, expr); + if expr.span < self.unwrap_or_span + && let ExprKind::Path(ref path) = expr.kind + && let QPath::Resolved(_, path) = path + && let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + && self.identifiers.contains(&local_id) + { + return ControlFlow::Break(()); } + rustc_hir::intravisit::walk_expr(self, expr) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 583e04fb4b1..e4326cb958e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,13 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{ + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, +}; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +18,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -26,7 +31,6 @@ pub(super) fn check<'tcx>( /// `or_insert(T::new())` or `or_insert(T::default())`. /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - #[allow(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: &str, @@ -70,18 +74,31 @@ pub(super) fn check<'tcx>( }; let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| { + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) .into_iter() .flatten() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .any(|assoc| { - assoc.fn_has_self_parameter + .find_map(|assoc| { + if assoc.fn_has_self_parameter && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + { + Some(assoc.def_id) + } else { + None + } }) - }); - if !has_suggested_method { + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir().get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { return false; } @@ -110,8 +127,8 @@ pub(super) fn check<'tcx>( } /// Checks for `*or(foo())`. - #[allow(clippy::too_many_arguments)] - fn check_general_case<'tcx>( + #[expect(clippy::too_many_arguments)] + fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, @@ -122,7 +139,7 @@ pub(super) fn check<'tcx>( span: Span, // None if lambda is required fun_span: Option<Span>, - ) { + ) -> bool { // (path, fn_has_argument, methods, suffix) const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ (sym::BTreeEntry, false, &["or_insert"], "with"), @@ -172,54 +189,68 @@ pub(super) fn check<'tcx>( format!("{name}_{suffix}({sugg})"), app, ); - } - } - - let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { - if let hir::ExprKind::Block( - hir::Block { - stmts: [], - expr: Some(expr), - .. - }, - _, - ) = arg.kind - { - expr + true } else { - arg + false } - }; + } if let [arg] = args { - let inner_arg = extract_inner_arg(arg); - match inner_arg.kind { - hir::ExprKind::Call(fun, or_args) => { - let or_has_args = !or_args.is_empty(); - if or_has_args - || !check_unwrap_or_default(cx, name, receiver, fun, Some(inner_arg), expr.span, method_span) - { - let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); - } - }, - hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) => { - check_unwrap_or_default(cx, name, receiver, inner_arg, None, expr.span, method_span); - }, - hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); - }, - _ => (), - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + // `or_fun_call` lint needs to take nested expr into account, + // but `unwrap_or_default` lint doesn't, we don't want something like: + // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by + // `opt.unwrap_or_default()`. + let is_nested_expr = ex.hir_id != inner_arg.hir_id; + + let is_triggered = match ex.kind { + hir::ExprKind::Call(fun, fun_args) => { + let inner_fun_has_args = !fun_args.is_empty(); + let fun_span = if inner_fun_has_args || is_nested_expr { + None + } else { + Some(fun.span) + }; + (!inner_fun_has_args + && !is_nested_expr + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) + }, + hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + }, + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) + }, + _ => false, + }; + + if is_triggered { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }); } // `map_or` takes two arguments if let [arg, lambda] = args { - let inner_arg = extract_inner_arg(arg); - if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { - let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; - check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + let is_top_most_expr = ex.hir_id == inner_arg.hir_id; + if let hir::ExprKind::Call(fun, fun_args) = ex.kind { + let fun_span = if fun_args.is_empty() && is_top_most_expr { + Some(fun.span) + } else { + None + }; + if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span) { + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + }); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs index 29f44ec2a4d..cfb823dbf5d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs @@ -21,7 +21,7 @@ pub(super) fn check( path: &Expr<'_>, expr: &Expr<'_>, msrv: &Msrv, - allowed_dotfiles: &FxHashSet<String>, + allowed_dotfiles: &FxHashSet<&'static str>, ) { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) && !path.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index e43b712021a..53fa444e93c 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; @@ -39,13 +40,19 @@ declare_clippy_lint! { } impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]); -#[derive(Clone)] pub struct MinIdentChars { - pub allowed_idents_below_min_chars: FxHashSet<String>, - pub min_ident_chars_threshold: u64, + allowed_idents_below_min_chars: &'static FxHashSet<String>, + min_ident_chars_threshold: u64, } impl MinIdentChars { + pub fn new(conf: &'static Conf) -> Self { + Self { + allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars, + min_ident_chars_threshold: conf.min_ident_chars_threshold, + } + } + #[expect(clippy::cast_possible_truncation)] fn is_ident_too_short(&self, cx: &LateContext<'_>, str: &str, span: Span) -> bool { !in_external_macro(cx.sess(), span) @@ -132,11 +139,11 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { return; } - // `struct Awa<T>(T)` - // ^ + // `struct Array<T, const N: usize>([T; N])` + // ^ ^ if let Node::PathSegment(path) = node { if let Res::Def(def_kind, ..) = path.res - && let DefKind::TyParam = def_kind + && let DefKind::TyParam | DefKind::ConstParam = def_kind { return; } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 32c8731c537..a9aafe7ed56 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -114,40 +114,35 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - span: Span, + _: Span, _: LocalDefId, ) { - if let FnKind::Closure = k { - // Does not apply to closures - return; - } - if in_external_macro(cx.tcx.sess, span) { - return; - } - for arg in iter_input_pats(decl, body) { - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { - return; - } - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind { - span_lint( - cx, - TOPLEVEL_REF_ARG, - arg.pat.span, - "`ref` directly on a function argument is ignored. \ - Consider using a reference type instead", - ); + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !in_external_macro(cx.tcx.sess, arg.span) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function argument is ignored. \ + Consider using a reference type instead", + ); + } } } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !in_external_macro(cx.tcx.sess, stmt.span) - && let StmtKind::Let(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index 934b9f490ad..748289454be 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -49,12 +49,12 @@ declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if !item.span.from_expansion() - && let ItemKind::Impl(imp) = &item.kind + if let ItemKind::Impl(imp) = &item.kind && let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind - && let Some(segment) = path.segments.iter().next() + && let [segment, ..] = path.segments && let Some(generic_args) = segment.args && !generic_args.args.is_empty() + && !item.span.from_expansion() { // get the name and span of the generic parameters in the Impl let mut impl_params = Vec::new(); diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index ed5f46ddc8d..68c158330ab 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; @@ -79,9 +80,10 @@ pub struct MissingConstForFn { } impl MissingConstForFn { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs index ab1b4aa3dee..954216038fb 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; use clippy_utils::qualify_min_const_fn::is_min_const_fn; @@ -49,9 +50,10 @@ pub struct MissingConstForThreadLocal { } impl MissingConstForThreadLocal { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 250fd5cbd48..2166b0fe5a0 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -5,6 +5,7 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; @@ -51,18 +52,10 @@ pub struct MissingDoc { prev_span: Option<Span>, } -impl Default for MissingDoc { - #[must_use] - fn default() -> Self { - Self::new(false) - } -} - impl MissingDoc { - #[must_use] - pub fn new(crate_items_only: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - crate_items_only, + crate_items_only: conf.missing_docs_in_crate_items, doc_hidden_stack: vec![false], prev_span: None, } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index c6c188854fd..cdf6645b3df 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,12 +1,13 @@ -use clippy_config::types::Rename; +use clippy_config::Conf; +use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{Item, ItemKind, UseKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -46,15 +47,18 @@ declare_clippy_lint! { } pub struct ImportRename { - conf_renames: Vec<Rename>, - renames: FxHashMap<DefId, Symbol>, + renames: DefIdMap<Symbol>, } impl ImportRename { - pub fn new(conf_renames: Vec<Rename>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_renames, - renames: FxHashMap::default(), + renames: conf + .enforced_import_renames + .iter() + .map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .collect(), } } } @@ -62,15 +66,6 @@ impl ImportRename { impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for Rename { path, rename } in &self.conf_renames { - let segs = path.split("::").collect::<Vec<_>>(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.renames.insert(id, Symbol::intern(rename)); - } - } - } - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for &res in &path.res { diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 2eb534da092..83af9979b9e 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -1,9 +1,10 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use clippy_utils::ty::InteriorMut; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; @@ -67,17 +68,12 @@ declare_clippy_lint! { } pub struct MutableKeyType<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability); - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { self.check_sig(cx, item.owner_id.def_id, sig.decl); @@ -107,10 +103,9 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { } impl<'tcx> MutableKeyType<'tcx> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), } } diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index bc7b2c6b7c1..60372121a7a 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -35,33 +35,18 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { - if in_external_macro(cx.sess(), ty.span) { - return; - } - - if let hir::TyKind::Ref( - _, - hir::MutTy { - ty: pty, - mutbl: hir::Mutability::Mut, - }, - ) = ty.kind + if let hir::TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == hir::Mutability::Mut + && let hir::TyKind::Ref(_, mty) = mty.ty.kind + && mty.mutbl == hir::Mutability::Mut + && !in_external_macro(cx.sess(), ty.span) { - if let hir::TyKind::Ref( - _, - hir::MutTy { - mutbl: hir::Mutability::Mut, - .. - }, - ) = pty.kind - { - span_lint( - cx, - MUT_MUT, - ty.span, - "generally you want to avoid `&mut &mut _` if possible", - ); - } + span_lint( + cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs index fb02f24c9dc..098098718af 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -37,83 +37,75 @@ declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { - if ref_pat.span.from_expansion() { - // OK, simple enough, lints doesn't check in macro. - return; - } - - // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { - let Node::Pat(pat) = node else { break }; - - if matches!(pat.kind, PatKind::Or(_)) { - return; + if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind + && !ref_pat.span.from_expansion() + && cx + .tcx + .hir() + .parent_iter(ref_pat.hir_id) + .map_while(|(_, parent)| if let Node::Pat(pat) = parent { Some(pat) } else { None }) + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + .all(|pat| !matches!(pat.kind, PatKind::Or(_))) + { + match pat.kind { + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + PatKind::Binding(BindingMode::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + ref_pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = ref_pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, } } - - // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { - return; - }; - - match pat.kind { - // Check sub_pat got a `ref` keyword (excluding `ref mut`). - PatKind::Binding(BindingMode::REF, _, ident, None) => { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - ref_pat.span, - "this pattern takes a reference on something that is being dereferenced", - |diag| { - // `&ref ident` - // ^^^^^ - let span = ref_pat.span.until(ident.span); - diag.span_suggestion_verbose( - span, - "try removing the `&ref` part", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` - PatKind::Slice( - before, - None - | Some(Pat { - kind: PatKind::Wild, .. - }), - after, - ) => { - check_subpatterns( - cx, - "dereferencing a slice pattern where every element takes a reference", - ref_pat, - pat, - itertools::chain(before, after), - ); - }, - PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { - check_subpatterns( - cx, - "dereferencing a tuple pattern where every element takes a reference", - ref_pat, - pat, - subpatterns, - ); - }, - PatKind::Struct(_, fields, _) => { - check_subpatterns( - cx, - "dereferencing a struct pattern where every field's pattern takes a reference", - ref_pat, - pat, - fields.iter().map(|field| field.pat), - ); - }, - _ => {}, - } } } 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 064ce59c234..67f9b52c352 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 @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::PossibleBorrowerMap; use clippy_utils::source::snippet_with_context; @@ -67,11 +68,10 @@ pub struct NeedlessBorrowsForGenericArgs<'tcx> { impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); impl NeedlessBorrowsForGenericArgs<'_> { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { possible_borrowers: Vec::new(), - msrv, + msrv: conf.msrv.clone(), } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 143acc2b1cb..6390e51f916 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -3,7 +3,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; @@ -55,16 +55,8 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { - return; - }; - - if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind - // Check the method name is `for_each`. - && method_name.ident.name == Symbol::intern("for_each") - // Check `for_each` is an associated function of `Iterator`. - && is_trait_method(cx, expr, sym::Iterator) - // Checks the receiver of `for_each` is also a method call. + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. @@ -72,6 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) + && method_name.ident.name.as_str() == "for_each" + && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 5ffd41d78e0..d543fd467ab 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,8 +1,10 @@ use super::needless_pass_by_value::requires_exact_signature; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; +use core::ops::ControlFlow; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -20,8 +22,6 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; -use core::ops::ControlFlow; - declare_clippy_lint! { /// ### What it does /// Check if a `&mut` function argument is actually used mutably. @@ -51,7 +51,6 @@ declare_clippy_lint! { "using a `&mut` argument when it's not mutated" } -#[derive(Clone)] pub struct NeedlessPassByRefMut<'tcx> { avoid_breaking_exported_api: bool, used_fn_def_ids: FxHashSet<LocalDefId>, @@ -59,9 +58,9 @@ pub struct NeedlessPassByRefMut<'tcx> { } impl NeedlessPassByRefMut<'_> { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, used_fn_def_ids: FxHashSet::default(), fn_def_ids_to_maybe_unused_mut: FxIndexMap::default(), } diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index f7621822b66..fa90ee60612 100644 --- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -45,10 +45,10 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Unary(UnOp::Not, inner) = expr.kind + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind && let ExprKind::Binary(ref op, left, _) = inner.kind && let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node + && !in_external_macro(cx.sess(), expr.span) { let ty = cx.typeck_results().expr_ty(left); diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 6f5505e8a63..15bb328b446 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -4,6 +4,7 @@ use std::ptr; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; @@ -179,17 +180,15 @@ fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { } pub struct NonCopyConst<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); impl<'tcx> NonCopyConst<'tcx> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), } } @@ -308,10 +307,6 @@ impl<'tcx> NonCopyConst<'tcx> { } impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); - } - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { if let ItemKind::Const(.., body_id) = it.kind { let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index eacfe9ff328..832518d2d35 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, @@ -73,13 +74,20 @@ declare_clippy_lint! { "unclear name" } -#[derive(Copy, Clone)] pub struct NonExpressiveNames { pub single_char_binding_names_threshold: u64, } impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]); +impl NonExpressiveNames { + pub fn new(conf: &'static Conf) -> Self { + Self { + single_char_binding_names_threshold: conf.single_char_binding_names_threshold, + } + } +} + struct ExistingName { interned: Symbol, span: Span, @@ -90,7 +98,7 @@ struct ExistingName { struct SimilarNamesLocalVisitor<'a, 'tcx> { names: Vec<ExistingName>, cx: &'a EarlyContext<'tcx>, - lint: NonExpressiveNames, + threshold: u64, /// A stack of scopes containing the single-character bindings in each scope. single_char_names: Vec<Vec<Ident>>, @@ -103,8 +111,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { } let num_single_char_names = self.single_char_names.iter().flatten().count(); - let threshold = self.lint.single_char_binding_names_threshold; - if num_single_char_names as u64 > threshold { + if num_single_char_names as u64 > self.threshold { let span = self .single_char_names .iter() @@ -384,7 +391,7 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(*self, cx, &item.attrs, &sig.decl, blk); + do_check(self, cx, &item.attrs, &sig.decl, blk); } } @@ -399,17 +406,17 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(*self, cx, &item.attrs, &sig.decl, blk); + do_check(self, cx, &item.attrs, &sig.decl, blk); } } } -fn do_check(lint: NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { +fn do_check(lint: &NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { if !attrs.iter().any(|attr| attr.has_name(sym::test)) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), cx, - lint, + threshold: lint.single_char_binding_names_threshold, single_char_names: vec![vec![]], }; diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 74e6c57b52d..a60988ac5db 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet; @@ -54,15 +55,14 @@ declare_clippy_lint! { "there is a field that is not safe to be sent to another thread in a `Send` struct" } -#[derive(Copy, Clone)] pub struct NonSendFieldInSendTy { enable_raw_pointer_heuristic: bool, } impl NonSendFieldInSendTy { - pub fn new(enable_raw_pointer_heuristic: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - enable_raw_pointer_heuristic, + enable_raw_pointer_heuristic: conf.enable_raw_pointer_heuristic_for_send, } } } diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 88f2eabaccb..2aaf1e6ff46 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,4 +1,5 @@ use clippy_config::types::MacroMatcher; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use rustc_ast::ast; @@ -35,17 +36,15 @@ declare_clippy_lint! { /// The (callsite span, (open brace, close brace), source snippet) type MacroInfo = (Span, (char, char), String); -#[derive(Debug)] pub struct MacroBraces { macro_braces: FxHashMap<String, (char, char)>, done: FxHashSet<Span>, } impl MacroBraces { - pub fn new(conf: &[MacroMatcher]) -> Self { - let macro_braces = macro_braces(conf); + pub fn new(conf: &'static Conf) -> Self { Self { - macro_braces, + macro_braces: macro_braces(&conf.standard_macro_braces), done: FxHashSet::default(), } } diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 7d6f26cde3e..a7e381be743 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,4 +1,5 @@ use super::ARITHMETIC_SIDE_EFFECTS; +use clippy_config::Conf; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; @@ -11,19 +12,9 @@ use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; -const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; -const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const DISALLOWED_INT_METHODS: &[Symbol] = &[ - sym::saturating_div, - sym::wrapping_div, - sym::wrapping_rem, - sym::wrapping_rem_euclid, -]; - -#[derive(Debug)] pub struct ArithmeticSideEffects { - allowed_binary: FxHashMap<String, FxHashSet<String>>, - allowed_unary: FxHashSet<String>, + allowed_binary: FxHashMap<&'static str, FxHashSet<&'static str>>, + allowed_unary: FxHashSet<&'static str>, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option<Span>, disallowed_int_methods: FxHashSet<Symbol>, @@ -33,26 +24,38 @@ pub struct ArithmeticSideEffects { impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl ArithmeticSideEffects { - #[must_use] - pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec<String>) -> Self { - let mut allowed_binary: FxHashMap<String, FxHashSet<String>> = <_>::default(); - for [lhs, rhs] in user_allowed_binary.into_iter().chain( - HARD_CODED_ALLOWED_BINARY - .iter() - .copied() - .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]), - ) { + pub fn new(conf: &'static Conf) -> Self { + let mut allowed_binary = FxHashMap::<&'static str, FxHashSet<&'static str>>::default(); + let mut allowed_unary = FxHashSet::<&'static str>::default(); + + allowed_unary.extend(["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]); + allowed_unary.extend(conf.arithmetic_side_effects_allowed_unary.iter().map(|x| &**x)); + allowed_binary.extend([ + ("f32", FxHashSet::from_iter(["f32"])), + ("f64", FxHashSet::from_iter(["f64"])), + ("std::string::String", FxHashSet::from_iter(["str"])), + ]); + for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary { allowed_binary.entry(lhs).or_default().insert(rhs); } - let allowed_unary = user_allowed_unary - .into_iter() - .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from)) - .collect(); + for s in &conf.arithmetic_side_effects_allowed { + allowed_binary.entry(s).or_default().insert("*"); + allowed_binary.entry("*").or_default().insert(s); + allowed_unary.insert(s); + } + Self { allowed_binary, allowed_unary, + disallowed_int_methods: [ + sym::saturating_div, + sym::wrapping_div, + sym::wrapping_rem, + sym::wrapping_rem_euclid, + ] + .into_iter() + .collect(), const_span: None, - disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(), expr_span: None, } } diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 59834781a58..9baecff801f 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -23,6 +23,7 @@ mod verbose_bit_mask; pub(crate) mod arithmetic_side_effects; +use clippy_config::Conf; use rustc_hir::{Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -841,6 +842,16 @@ pub struct Operators { verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool, } +impl Operators { + pub fn new(conf: &'static Conf) -> Self { + Self { + arithmetic_context: numeric_arithmetic::Context::default(), + verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold, + modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero, + } + } +} + impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, ARITHMETIC_SIDE_EFFECTS, @@ -869,15 +880,7 @@ impl_lint_pass!(Operators => [ PTR_EQ, SELF_ASSIGNMENT, ]); -impl Operators { - pub fn new(verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool) -> Self { - Self { - arithmetic_context: numeric_arithmetic::Context::default(), - verbose_bit_mask_threshold, - modulo_arithmetic_allow_comparison_to_zero, - } - } -} + impl<'tcx> LateLintPass<'tcx> for Operators { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { eq_op::check_assert(cx, e); diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs index 4bfb26209d2..d16f5f8e112 100644 --- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -3,7 +3,7 @@ use clippy_utils::is_direct_expn_of; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -35,30 +35,18 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - fn lint(cx: &EarlyContext<'_>, span: Span) { + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind + && matches!(seg.ident.name, sym::expect | sym::unwrap) + && is_direct_expn_of(receiver.span, "option_env").is_some() + { span_lint_and_help( cx, OPTION_ENV_UNWRAP, - span, + expr.span, "this will panic at run-time if the environment variable doesn't exist at compile-time", None, "consider using the `env!` macro instead", ); } - - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind - && matches!(seg.ident.name, sym::expect | sym::unwrap) - { - if let ExprKind::Call(caller, _) = &receiver.kind && - // If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } - } } } diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 80ef761906e..4eefd0065f6 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -5,9 +6,16 @@ use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -#[derive(Clone)] pub struct PanicUnimplemented { - pub allow_panic_in_tests: bool, + allow_panic_in_tests: bool, +} + +impl PanicUnimplemented { + pub fn new(conf: &'static Conf) -> Self { + Self { + allow_panic_in_tests: conf.allow_panic_in_tests, + } + } } declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 128bfd49d9e..5ca244f0141 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,5 +1,6 @@ use std::{cmp, iter}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; @@ -14,7 +15,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, RegionKind}; +use rustc_middle::ty::{self, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; @@ -103,7 +104,6 @@ declare_clippy_lint! { "functions taking large arguments by value" } -#[derive(Copy, Clone)] pub struct PassByRefOrValue { ref_min_size: u64, value_max_size: u64, @@ -111,14 +111,9 @@ pub struct PassByRefOrValue { } impl<'tcx> PassByRefOrValue { - pub fn new( - ref_min_size: Option<u64>, - value_max_size: u64, - avoid_breaking_exported_api: bool, - pointer_width: u32, - ) -> Self { - let ref_min_size = ref_min_size.unwrap_or_else(|| { - let bit_width = u64::from(pointer_width); + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| { + let bit_width = u64::from(tcx.sess.target.pointer_width); // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); @@ -130,8 +125,8 @@ impl<'tcx> PassByRefOrValue { Self { ref_min_size, - value_max_size, - avoid_breaking_exported_api, + value_max_size: conf.pass_by_value_size_limit, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs new file mode 100644 index 00000000000..0008f154ae3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -0,0 +1,193 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::{LitKind, StrStyle}; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `push` immediately after creating a new `PathBuf`. + /// + /// ### Why is this bad? + /// Multiple `.join()` calls are usually easier to read than multiple `.push` + /// calls across multiple statements. It might also be possible to use + /// `PathBuf::from` instead. + /// + /// ### Known problems + /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternativly be + /// used when the `PathBuf` is newly constructed. This will avoild the implicit clone. + /// + /// ### Example + /// ```rust + /// # use std::path::PathBuf; + /// let mut path_buf = PathBuf::new(); + /// path_buf.push("foo"); + /// ``` + /// Use instead: + /// ```rust + /// # use std::path::PathBuf; + /// let path_buf = PathBuf::from("foo"); + /// // or + /// let path_buf = PathBuf::new().join("foo"); + /// ``` + #[clippy::version = "1.81.0"] + pub PATHBUF_INIT_THEN_PUSH, + restriction, + "`push` immediately after `PathBuf` creation" +} + +impl_lint_pass!(PathbufThenPush<'_> => [PATHBUF_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct PathbufThenPush<'tcx> { + searcher: Option<PathbufPushSearcher<'tcx>>, +} + +struct PathbufPushSearcher<'tcx> { + local_id: HirId, + lhs_is_let: bool, + let_ty_span: Option<Span>, + init_val: Expr<'tcx>, + arg: Option<Expr<'tcx>>, + name: Symbol, + err_span: Span, +} + +impl<'tcx> PathbufPushSearcher<'tcx> { + /// Try to generate a suggestion with `PathBuf::from`. + /// Returns `None` if the suggestion would be invalid. + fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option<String> { + if let ExprKind::Call(iter_expr, []) = &self.init_val.kind + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = &iter_expr.kind + && let TyKind::Path(ty_path) = &ty.kind + && let QPath::Resolved(None, path) = ty_path + && let Res::Def(_, def_id) = &path.res + && cx.tcx.is_diagnostic_item(sym::PathBuf, *def_id) + && segment.ident.name == sym::new + && let Some(arg) = self.arg + && let ExprKind::Lit(x) = arg.kind + && let LitKind::Str(_, StrStyle::Cooked) = x.node + && let Some(s) = snippet_opt(cx, arg.span) + { + Some(format!(" = PathBuf::from({s});")) + } else { + None + } + } + + fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> { + let arg = self.arg?; + let arg_str = snippet_opt(cx, arg.span)?; + let init_val = snippet_opt(cx, self.init_val.span)?; + Some(format!(" = {init_val}.join({arg_str});")) + } + + fn display_err(&self, cx: &LateContext<'_>) { + if clippy_utils::attrs::span_contains_cfg(cx, self.err_span) { + return; + } + let mut sugg = if self.lhs_is_let { + String::from("let mut ") + } else { + String::new() + }; + sugg.push_str(self.name.as_str()); + if let Some(span) = self.let_ty_span { + sugg.push_str(": "); + sugg.push_str(&snippet(cx, span, "_")); + } + match self.gen_pathbuf_from(cx) { + Some(value) => { + sugg.push_str(&value); + }, + None => { + if let Some(value) = self.gen_pathbuf_join(cx) { + sugg.push_str(&value); + } else { + return; + } + }, + } + + span_lint_and_sugg( + cx, + PATHBUF_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `.join()`", + sugg, + Applicability::HasPlaceholders, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { + fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + self.searcher = None; + } + + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { + if let Some(init_expr) = local.init + && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind + && !in_external_macro(cx.sess(), local.span) + && let ty = cx.typeck_results().pat_ty(local.pat) + && is_type_diagnostic_item(cx, ty, sym::PathBuf) + { + self.searcher = Some(PathbufPushSearcher { + local_id: id, + lhs_is_let: true, + name: name.name, + let_ty_span: local.ty.map(|ty| ty.span), + err_span: local.span, + init_val: *init_expr, + arg: None, + }); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(left, right, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind + && let [name] = &path.segments + && let Res::Local(id) = path.res + && !in_external_macro(cx.sess(), expr.span) + && let ty = cx.typeck_results().expr_ty(left) + && is_type_diagnostic_item(cx, ty, sym::PathBuf) + { + self.searcher = Some(PathbufPushSearcher { + local_id: id, + lhs_is_let: false, + let_ty_span: None, + name: name.ident.name, + err_span: expr.span, + init_val: *right, + arg: None, + }); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(mut searcher) = self.searcher.take() { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + searcher.err_span = searcher.err_span.to(stmt.span); + searcher.arg = Some(*arg_expr); + searcher.display_err(cx); + } + } + } + + fn check_block_post(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + self.searcher = None; + } +} diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs index 704acdc103e..dc142b6e157 100644 --- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs +++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs @@ -31,10 +31,10 @@ declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALS impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) - && path.ident.name == sym!(set_readonly) && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node + && path.ident.name.as_str() == "set_readonly" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index d20d4a605a2..77b707567e4 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -1,4 +1,5 @@ use clippy_config::types::PubUnderscoreFieldsBehaviour; +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_path_lang_item; @@ -42,10 +43,18 @@ declare_clippy_lint! { } pub struct PubUnderscoreFields { - pub behavior: PubUnderscoreFieldsBehaviour, + behavior: PubUnderscoreFieldsBehaviour, } impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]); +impl PubUnderscoreFields { + pub fn new(conf: &'static Conf) -> Self { + Self { + behavior: conf.pub_underscore_fields_behavior, + } + } +} + impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { // This lint only pertains to structs. diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 7cf98ad9e09..ef6b4d3aeab 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -2,6 +2,7 @@ use crate::manual_let_else::MANUAL_LET_ELSE; use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::msrvs::Msrv; use clippy_config::types::MatchLintBehaviour; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -62,11 +63,10 @@ pub struct QuestionMark { impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); impl QuestionMark { - #[must_use] - pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, - matches_behaviour, + msrv: conf.msrv.clone(), + matches_behaviour: conf.matches_for_let_else, try_block_depth_stack: Vec::new(), } } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 4fdaa9f00a1..829fb58bc65 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; @@ -164,9 +165,10 @@ pub struct Ranges { } impl Ranges { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 3a004245459..3c19ee3522d 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -1,8 +1,6 @@ -use std::iter::once; -use std::ops::ControlFlow; - +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -10,6 +8,8 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; +use std::iter::once; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -61,22 +61,27 @@ pub struct RawStrings { pub allow_one_hash_in_raw_strings: bool, } +impl RawStrings { + pub fn new(conf: &'static Conf) -> Self { + Self { + allow_one_hash_in_raw_strings: conf.allow_one_hash_in_raw_strings, + } + } +} + impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Lit(lit) = expr.kind - && let LitKind::StrRaw(max) | LitKind::ByteStrRaw(max) | LitKind::CStrRaw(max) = lit.kind + if let ExprKind::Lit(lit) = expr.kind + && let (prefix, max) = match lit.kind { + LitKind::StrRaw(max) => ("r", max), + LitKind::ByteStrRaw(max) => ("br", max), + LitKind::CStrRaw(max) => ("cr", max), + _ => return, + } + && !in_external_macro(cx.sess(), expr.span) + && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) { let str = lit.symbol.as_str(); - let prefix = match lit.kind { - LitKind::StrRaw(..) => "r", - LitKind::ByteStrRaw(..) => "br", - LitKind::CStrRaw(..) => "cr", - _ => unreachable!(), - }; - if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) { - return; - } let descr = lit.kind.descr(); if !str.contains(['\\', '"']) { @@ -86,7 +91,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, 0, max); + let (start, end) = hash_spans(expr.span, prefix.len(), 0, max); // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); @@ -148,7 +153,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary hashes around raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, req, max); + let (start, end) = hash_spans(expr.span, prefix.len(), req, max); let message = match max - req { _ if req == 0 => format!("remove all the hashes around the {descr} literal"), @@ -174,11 +179,11 @@ impl EarlyLintPass for RawStrings { /// r###".."### /// ^^ ^^ /// ``` -fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) { +fn hash_spans(literal_span: Span, prefix_len: usize, req: u8, max: u8) -> (Span, Span) { let literal_span = literal_span.data(); // BytePos: we checked prefix appears literally in the source text - let hash_start = literal_span.lo + BytePos::from_usize(prefix.len()); + let hash_start = literal_span.lo + BytePos::from_usize(prefix_len); let hash_end = literal_span.hi; // BytePos: req/max are counts of the ASCII character # diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 47d3ed08b8e..bad9b979203 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -14,6 +14,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::ExpnKind; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -42,24 +43,15 @@ declare_clippy_lint! { declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); // Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} +struct ReturnVisitor; impl<'tcx> Visitor<'tcx> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> ControlFlow<()> { if let ExprKind::Ret(_) | ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind { - self.found_return = true; - } else { - hir_visit::walk_expr(self, ex); + return ControlFlow::Break(()); } + hir_visit::walk_expr(self, ex) } } @@ -101,9 +93,8 @@ fn find_innermost_closure<'tcx>( while let ExprKind::Closure(closure) = expr.kind && let body = cx.tcx.hir().body(closure.body) && { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(body.value); - !visitor.found_return + let mut visitor = ReturnVisitor; + !visitor.visit_expr(body.value).is_break() } && steps > 0 { diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index 17b031d5f01..0e637538615 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; @@ -40,9 +41,10 @@ pub struct RedundantFieldNames { } impl RedundantFieldNames { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index 136e7db83bd..d6e741dd974 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind}; @@ -38,9 +39,10 @@ pub struct RedundantStaticLifetimes { } impl RedundantStaticLifetimes { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index e925ec0271c..8cacb646f51 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform // the operation once and store the results let mut resolve = |path, kind| { - for id in def_path_def_ids(cx, path) { + for id in def_path_def_ids(cx.tcx, path) { self.definitions.insert(id, kind); } }; diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 0e77acdfd77..7615c21276d 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; @@ -64,21 +65,20 @@ declare_clippy_lint! { } impl_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); -#[derive(Copy, Clone)] pub struct SemicolonBlock { semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool, } impl SemicolonBlock { - pub fn new(semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, + semicolon_inside_block_ignore_singleline: conf.semicolon_inside_block_ignore_singleline, + semicolon_outside_block_ignore_multiline: conf.semicolon_outside_block_ignore_multiline, } } - fn semicolon_inside_block(self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { + fn semicolon_inside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); @@ -103,7 +103,7 @@ impl SemicolonBlock { } fn semicolon_outside_block( - self, + &self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index f1ec91d7aff..fcf1c4cc5c2 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -32,7 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx, &paths::SERDE_DE_VISITOR) { + if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) { if did == visit_did { let mut seen_str = None; let mut seen_string = None; diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs index 23bbeae0e31..abe13a97c0d 100644 --- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs +++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_from_proc_macro, is_in_test_function}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; @@ -66,13 +67,19 @@ pub enum CallState { Multiple, } -#[derive(Clone)] pub struct SingleCallFn { - pub avoid_breaking_exported_api: bool, - pub def_id_to_usage: FxIndexMap<LocalDefId, CallState>, + avoid_breaking_exported_api: bool, + def_id_to_usage: FxIndexMap<LocalDefId, CallState>, } impl SingleCallFn { + pub fn new(conf: &'static Conf) -> Self { + Self { + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + def_id_to_usage: FxIndexMap::default(), + } + } + fn is_function_allowed( &self, cx: &LateContext<'_>, 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 0a9a3c6307a..e0558429638 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 @@ -99,7 +99,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = snippet_opt(cx, start.span) && let Some(end_snippet) = snippet_opt(cx, end.span) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 7ba58942a17..7e211d64da1 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; @@ -75,9 +76,10 @@ pub struct StringPatterns { } impl StringPatterns { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 07390d6f430..59b74122f30 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; @@ -86,16 +87,17 @@ declare_clippy_lint! { "check if the same trait bounds are specified more than once during a generic declaration" } -#[derive(Clone)] pub struct TraitBounds { max_trait_bounds: u64, msrv: Msrv, } impl TraitBounds { - #[must_use] - pub fn new(max_trait_bounds: u64, msrv: Msrv) -> Self { - Self { max_trait_bounds, msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + max_trait_bounds: conf.max_trait_bounds, + msrv: conf.msrv.clone(), + } } } @@ -181,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // Iterate the bounds and add them to our seen hash // If we haven't yet seen it, add it to the fixed traits - for bound in bounds { + for (bound, _) in bounds { let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; @@ -196,9 +198,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // If the number of unique traits isn't the same as the number of traits in the bounds, // there must be 1 or more duplicates if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].span; + let mut bounds_span = bounds[0].0.span; - for bound in bounds.iter().skip(1) { + for (bound, _) in bounds.iter().skip(1) { bounds_span = bounds_span.to(bound.span); } diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index aa329ec3366..9c6813a54b9 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -20,6 +20,7 @@ mod utils; mod wrong_transmute; use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::in_constant; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -577,9 +578,10 @@ impl_lint_pass!(Transmute => [ MISSING_TRANSMUTE_ANNOTATIONS, ]); impl Transmute { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } impl<'tcx> LateLintPass<'tcx> for Transmute { @@ -615,7 +617,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) + | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, &self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 1476ea8e7a4..0772b284968 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -1,10 +1,11 @@ use super::TRANSMUTE_PTR_TO_PTR; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; /// Checks for `transmute_ptr_to_ptr` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -14,9 +15,10 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + msrv: &Msrv, ) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::RawPtr(_, _), ty::RawPtr(to_ty, to_mutbl)) => { + match (from_ty.kind(), to_ty.kind()) { + (ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, @@ -24,8 +26,39 @@ pub(super) fn check<'tcx>( "transmute from a pointer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty, *to_mutbl)); - diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); + if from_mutbl == to_mutbl + && to_pointee_ty.is_sized(cx.tcx, cx.param_env) + && msrv.meets(msrvs::POINTER_CAST) + { + diag.span_suggestion_verbose( + e.span, + "use `pointer::cast` instead", + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()), + Applicability::MaybeIncorrect, + ); + } else if from_pointee_ty == to_pointee_ty + && let Some(method) = match (from_mutbl, to_mutbl) { + (ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"), + (ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"), + _ => None, + } + && !from_pointee_ty.has_erased_regions() + && msrv.meets(msrvs::POINTER_CAST_CONSTNESS) + { + diag.span_suggestion_verbose( + e.span, + format!("use `pointer::{method}` instead"), + format!("{}.{method}()", arg.maybe_par()), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion_verbose( + e.span, + "use an `as` cast instead", + arg.as_ty(to_ty), + Applicability::MaybeIncorrect, + ); + } } }, ); diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 564b065d0ba..1d0de932754 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{is_from_proc_macro, path_to_local}; @@ -42,9 +43,15 @@ declare_clippy_lint! { } impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]); -#[derive(Clone)] pub struct TupleArrayConversions { - pub msrv: Msrv, + msrv: Msrv, +} +impl TupleArrayConversions { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } } impl LateLintPass<'_> for TupleArrayConversions { diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 801e8862619..83cc9f2d8df 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -81,14 +81,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { - if let TyKind::TraitObject(traits, ..) = t.kind - && !traits.is_empty() - && let Some(trait_did) = traits[0].trait_ref.trait_def_id() - // Only Send/Sync can be used as additional traits, so it is enough to - // check only the first trait. - && cx.tcx.is_diagnostic_item(sym::Any, trait_did) - { - return true; + if let TyKind::TraitObject(traits, ..) = t.kind { + return traits + .iter() + .any(|(bound, _)| { + if let Some(trait_did) = bound.trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; + } + false + }); } false diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 62ef65ca122..3a14927802b 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -9,6 +9,7 @@ mod type_complexity; mod utils; mod vec_box; +use clippy_config::Conf; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -446,11 +447,11 @@ impl<'tcx> LateLintPass<'tcx> for Types { } impl Types { - pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - vec_box_size_threshold, - type_complexity_threshold, - avoid_breaking_exported_api, + vec_box_size_threshold: conf.vec_box_size_threshold, + type_complexity_threshold: conf.type_complexity_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index b6e765d7c39..7fcfd5c8f35 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -55,6 +55,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound + .0 .bound_generic_params .iter() .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 6ba98a92423..42100e1d755 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -16,6 +16,7 @@ use rustc_session::impl_lint_pass; use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, Span}; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -276,7 +277,6 @@ struct CheckCalls<'a, 'tcx> { cx: &'a LateContext<'tcx>, map: Map<'tcx>, implemented_ty_id: DefId, - found_default_call: bool, method_span: Span, } @@ -285,16 +285,14 @@ where 'tcx: 'a, { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.map } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.found_default_call { - return; - } - walk_expr(self, expr); + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { + walk_expr(self, expr)?; if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind @@ -303,9 +301,10 @@ where && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { - self.found_default_call = true; span_error(self.cx, self.method_span, expr); + return ControlFlow::Break(()); } + ControlFlow::Continue(()) } } @@ -383,7 +382,6 @@ impl UnconditionalRecursion { cx, map: cx.tcx.hir(), implemented_ty_id, - found_default_call: false, method_span, }; walk_body(&mut c, body); diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 93a1089a970..3ab30bf5dba 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; @@ -91,17 +92,16 @@ declare_clippy_lint! { "annotating safe code with a safety comment" } -#[derive(Copy, Clone)] pub struct UndocumentedUnsafeBlocks { accept_comment_above_statement: bool, accept_comment_above_attributes: bool, } impl UndocumentedUnsafeBlocks { - pub fn new(accept_comment_above_statement: bool, accept_comment_above_attributes: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - accept_comment_above_statement, - accept_comment_above_attributes, + accept_comment_above_statement: conf.accept_comment_above_statement, + accept_comment_above_attributes: conf.accept_comment_above_attributes, } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index bfcefb26153..3f130bf5a67 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; @@ -47,10 +48,10 @@ pub struct UnnecessaryBoxReturns { impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]); impl UnnecessaryBoxReturns { - pub fn new(avoid_breaking_exported_api: bool, maximum_size: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, - maximum_size, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + maximum_size: conf.unnecessary_box_size, } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index 2da75334344..afdd3505cdd 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -2,14 +2,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use clippy_utils::{get_parent_expr, path_to_local}; -use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind, UnOp}; +use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for initialization of a `struct` by copying a base without setting - /// any field. + /// Checks for initialization of an identical `struct` from another instance + /// of the type, either by copying a base without setting any field or by + /// moving all fields individually. /// /// ### Why is this bad? /// Readability suffers from unnecessary struct building. @@ -29,9 +30,14 @@ declare_clippy_lint! { /// let b = a; /// ``` /// + /// The struct literal ``S { ..a }`` in the assignment to ``b`` could be replaced + /// with just ``a``. + /// /// ### Known Problems /// Has false positives when the base is a place expression that cannot be /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547). + /// + /// Empty structs are ignored by the lint. #[clippy::version = "1.70.0"] pub UNNECESSARY_STRUCT_INITIALIZATION, nursery, @@ -41,42 +47,111 @@ declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]); impl LateLintPass<'_> for UnnecessaryStruct { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Struct(_, &[], Some(base)) = expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) - && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) - && parent_ty.is_any_ptr() - { - if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() { - // When the type implements `Copy`, a reference to the new struct works on the - // copy. Using the original would borrow it. - return; - } - - if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) { - // The original can be used in a mutable reference context only if it is mutable. - return; - } - } + let ExprKind::Struct(_, fields, base) = expr.kind else { + return; + }; - // TODO: do not propose to replace *XX if XX is not Copy - if let ExprKind::Unary(UnOp::Deref, target) = base.kind - && matches!(target.kind, ExprKind::Path(..)) - && !is_copy(cx, cx.typeck_results().expr_ty(expr)) - { - // `*base` cannot be used instead of the struct in the general case if it is not Copy. - return; - } + if expr.span.from_expansion() { + // Prevent lint from hitting inside macro code + return; + } + + let field_path = same_path_in_all_fields(cx, expr, fields); + + let sugg = match (field_path, base) { + (Some(&path), None) => { + // all fields match, no base given + path.span + }, + (Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => { + // all fields match, has base: ensure that the path of the base matches + base.span + }, + (None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => { + // just the base, no explicit fields + base.span + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_STRUCT_INITIALIZATION, + expr.span, + "unnecessary struct building", + "replace with", + snippet(cx, sugg, "..").into_owned(), + rustc_errors::Applicability::MachineApplicable, + ); + } +} + +fn base_is_suitable(cx: &LateContext<'_>, expr: &Expr<'_>, base: &Expr<'_>) -> bool { + if !check_references(cx, expr, base) { + return false; + } + + // TODO: do not propose to replace *XX if XX is not Copy + if let ExprKind::Unary(UnOp::Deref, target) = base.kind + && matches!(target.kind, ExprKind::Path(..)) + && !is_copy(cx, cx.typeck_results().expr_ty(expr)) + { + // `*base` cannot be used instead of the struct in the general case if it is not Copy. + return false; + } + true +} + +/// Check whether all fields of a struct assignment match. +/// Returns a [Path] item that one can obtain a span from for the lint suggestion. +/// +/// Conditions that must be satisfied to trigger this variant of the lint: +/// +/// - source struct of the assignment must be of same type as the destination +/// - names of destination struct fields must match the field names of the source +/// +/// We don’t check here if all struct fields are assigned as the remainder may +/// be filled in from a base struct. +fn same_path_in_all_fields<'tcx>( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fields: &[ExprField<'tcx>], +) -> Option<&'tcx Path<'tcx>> { + let ty = cx.typeck_results().expr_ty(expr); + + let mut found = None; - span_lint_and_sugg( - cx, - UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, - "unnecessary struct building", - "replace with", - snippet(cx, base.span, "..").into_owned(), - rustc_errors::Applicability::MachineApplicable, - ); + for f in fields { + // fields are assigned from expression + if let ExprKind::Field(src_expr, ident) = f.expr.kind + // expression type matches + && ty == cx.typeck_results().expr_ty(src_expr) + // field name matches + && f.ident == ident + // assigned from a path expression + && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind + { + let Some((_, p)) = found else { + // this is the first field assignment in the list + found = Some((src_expr, src_path)); + continue; + }; + + if p.res == src_path.res { + // subsequent field assignment with same origin struct as before + continue; + } } + // source of field assignment doesn’t qualify + return None; + } + + if let Some((src_expr, src_path)) = found + && check_references(cx, expr, src_expr) + { + Some(src_path) + } else { + None } } @@ -89,3 +164,43 @@ fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, expr_a) + && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) + && parent_ty.is_any_ptr() + { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + // When the type implements `Copy`, a reference to the new struct works on the + // copy. Using the original would borrow it. + return false; + } + + if parent_ty.is_mutable_ptr() && !is_mutable(cx, expr_b) { + // The original can be used in a mutable reference context only if it is mutable. + return false; + } + } + + true +} + +/// When some fields are assigned from a base struct and others individually +/// the lint applies only if the source of the field is the same as the base. +/// This is enforced here by comparing the path of the base expression; +/// needless to say the lint only applies if it (or whatever expression it is +/// a reference of) actually has a path. +fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { + let base_path = match base.kind { + ExprKind::Unary(UnOp::Deref, base_expr) => { + if let ExprKind::Path(QPath::Resolved(_, base_path)) = base_expr.kind { + base_path + } else { + return false; + } + }, + ExprKind::Path(QPath::Resolved(_, base_path)) => base_path, + _ => return false, + }; + path.res == base_path.res +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 5c7fbbab988..e4e7f7d06e7 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; @@ -63,9 +64,9 @@ pub struct UnnecessaryWraps { impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); impl UnnecessaryWraps { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } } 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 842046c941d..98f0be3135d 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -1,6 +1,7 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; @@ -51,9 +52,10 @@ pub struct UnnestedOrPatterns { } impl UnnestedOrPatterns { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index e6f799335d7..86a811e17ca 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -8,6 +8,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; use rustc_span::sym; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -70,15 +71,16 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { return; } - for stmt in &block.stmts[idx..] { - vis.visit_stmt(stmt); - } + let mut found_peek_call = block.stmts[idx..].iter().any(|stmt| vis.visit_stmt(stmt).is_break()); - if let Some(expr) = block.expr { - vis.visit_expr(expr); + if !found_peek_call + && let Some(expr) = block.expr + && vis.visit_expr(expr).is_break() + { + found_peek_call = true; } - if !vis.found_peek_call { + if !found_peek_call { span_lint_hir_and_then( cx, UNUSED_PEEKABLE, @@ -98,31 +100,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { struct PeekableVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, expected_hir_id: HirId, - found_peek_call: bool, } impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self { - Self { - cx, - expected_hir_id, - found_peek_call: false, - } + Self { cx, expected_hir_id } } } impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { type NestedFilter = OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.found_peek_call { - return; - } - + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { if path_to_local_id(ex, self.expected_hir_id) { for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) { match node { @@ -137,14 +131,14 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { && func_did == into_iter_did { // Probably a for loop desugar, stop searching - return; + return ControlFlow::Continue(()); } if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) { - self.found_peek_call = true; + return ControlFlow::Break(()); } - return; + return ControlFlow::Continue(()); }, // Catch anything taking a Peekable mutably ExprKind::MethodCall( @@ -162,16 +156,14 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") && arg_is_mut_peekable(self.cx, self_arg) { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); } // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) && !is_trait_method(self.cx, expr, sym::Iterator) { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); } // foo.by_ref(), keep checking for `peek` @@ -179,41 +171,42 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { continue; } - return; + return ControlFlow::Continue(()); }, ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => { }, - ExprKind::AddrOf(_, Mutability::Not, _) => return, + ExprKind::AddrOf(_, Mutability::Not, _) => return ControlFlow::Continue(()), _ => { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); }, } }, Node::LetStmt(LetStmt { init: Some(init), .. }) => { if arg_is_mut_peekable(self.cx, init) { - self.found_peek_call = true; + return ControlFlow::Break(()); } - return; + return ControlFlow::Continue(()); }, Node::Stmt(stmt) => { match stmt.kind { - StmtKind::Let(_) | StmtKind::Item(_) => self.found_peek_call = true, + StmtKind::Let(_) | StmtKind::Item(_) => { + return ControlFlow::Break(()); + }, StmtKind::Expr(_) | StmtKind::Semi(_) => {}, } - return; + return ControlFlow::Continue(()); }, Node::Block(_) | Node::ExprField(_) => {}, _ => { - return; + return ControlFlow::Continue(()); }, } } } - walk_expr(self, ex); + walk_expr(self, ex) } } diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index 3e6102f5982..781f51aa9b0 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; @@ -43,9 +44,9 @@ pub struct UnusedSelf { impl_lint_pass!(UnusedSelf => [UNUSED_SELF]); impl UnusedSelf { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } } diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index 72392f8e1f7..8de062a8fc1 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use core::mem::replace; use rustc_errors::Applicability; @@ -39,17 +40,16 @@ declare_clippy_lint! { "capitalized acronyms are against the naming convention" } -#[derive(Default)] pub struct UpperCaseAcronyms { avoid_breaking_exported_api: bool, upper_case_acronyms_aggressive: bool, } impl UpperCaseAcronyms { - pub fn new(avoid_breaking_exported_api: bool, aggressive: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, - upper_case_acronyms_aggressive: aggressive, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + upper_case_acronyms_aggressive: conf.upper_case_acronyms_aggressive, } } } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 0bab917607d..93785b45c27 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; @@ -60,10 +61,9 @@ pub struct UseSelf { } impl UseSelf { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), stack: Vec::new(), } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 07879e81fc2..b017a6bf665 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - for def_id in def_path_def_ids(cx, module) { + for def_id in def_path_def_ids(cx.tcx, module) { for item in cx.tcx.module_children(def_id) { if let Res::Def(DefKind::Const, item_def_id) = item.res && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 0beb0bb8ed4..980437259c3 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx, path).is_empty() { + if !def_path_res(cx.tcx, path).is_empty() { return true; } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 8cf42832761..41183700f09 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -108,7 +108,7 @@ impl UnnecessaryDefPath { // Extract the path to the matched type && let Some(segments) = path_to_matched_type(cx, item_arg) && let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>() - && let Some(def_id) = def_path_def_ids(cx, &segments[..]).next() + && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() { // Check if the target item is a diagnostic item or LangItem. #[rustfmt::skip] @@ -206,7 +206,7 @@ impl UnnecessaryDefPath { fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { let Some(path) = path_from_array(elements) else { return }; - for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { + for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { self.array_def_ids.insert((def_id, span)); } } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 9edf7579d48..a831234906b 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; @@ -17,12 +18,21 @@ use rustc_session::impl_lint_pass; use rustc_span::{sym, DesugaringKind, Span}; #[expect(clippy::module_name_repetitions)] -#[derive(Clone)] pub struct UselessVec { - pub too_large_for_stack: u64, - pub msrv: Msrv, - pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, - pub allow_in_test: bool, + too_large_for_stack: u64, + msrv: Msrv, + span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, + allow_in_test: bool, +} +impl UselessVec { + pub fn new(conf: &'static Conf) -> Self { + Self { + too_large_for_stack: conf.too_large_for_stack, + msrv: conf.msrv.clone(), + span_to_lint_map: BTreeMap::new(), + allow_in_test: conf.allow_useless_vec_in_tests, + } + } } declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index a0a60ca8875..c4d64ee4609 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_in_test; use clippy_utils::source::{snippet, snippet_with_applicability}; @@ -97,17 +98,16 @@ declare_clippy_lint! { "lint `use _::*` statements" } -#[derive(Default)] pub struct WildcardImports { warn_on_all: bool, - allowed_segments: FxHashSet<String>, + allowed_segments: &'static FxHashSet<String>, } impl WildcardImports { - pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet<String>) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - warn_on_all, - allowed_segments: allowed_wildcard_imports, + warn_on_all: conf.warn_on_all_wildcard_imports, + allowed_segments: &conf.allowed_wildcard_imports, } } } @@ -181,7 +181,7 @@ impl WildcardImports { fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { item.span.from_expansion() || is_prelude_import(segments) - || is_allowed_via_config(segments, &self.allowed_segments) + || is_allowed_via_config(segments, self.allowed_segments) || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) } } diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 96e53b7ef0b..54e7e92f0c4 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_test; use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; @@ -237,7 +238,6 @@ declare_clippy_lint! { "writing a literal with a format string" } -#[derive(Default)] pub struct Write { format_args: FormatArgsStorage, in_debug_impl: bool, @@ -245,11 +245,11 @@ pub struct Write { } impl Write { - pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self { + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests, - ..Default::default() + allow_print_in_tests: conf.allow_print_in_tests, + in_debug_impl: false, } } } diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index ad041e55bda..fdc1d06e67a 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -3,10 +3,11 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Node}; +use rustc_hir::{ArrayLen, ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ConstKind, Ty}; +use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -45,18 +46,27 @@ declare_clippy_lint! { declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]); impl LateLintPass<'_> for ZeroRepeatSideEffects { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>) { + let hir_map = cx.tcx.hir(); if let Some(args) = VecArgs::hir(cx, expr) && let VecArgs::Repeat(inner_expr, len) = args && let ExprKind::Lit(l) = len.kind - && let LitKind::Int(i, _) = l.node - && i.0 == 0 + && let LitKind::Int(Pu128(0), _) = l.node { inner_check(cx, expr, inner_expr, true); - } else if let ExprKind::Repeat(inner_expr, _) = expr.kind - && let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind() - && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() - && element_count.to_target_usize(cx.tcx) == 0 + } + // Lint only if the length is a literal zero, and not a path to any constants. + // NOTE(@y21): When reading `[f(); LEN]`, I intuitively expect that the function is called and it + // doesn't seem as confusing as `[f(); 0]`. It would also have false positives when eg. + // the const item depends on `#[cfg]s` and has different values in different compilation + // sessions). + else if let ExprKind::Repeat(inner_expr, length) = expr.kind + && let ArrayLen::Body(const_arg) = length + && let ConstArgKind::Anon(anon_const) = const_arg.kind + && let length_expr = hir_map.body(anon_const.body).value + && !length_expr.span.from_expansion() + && let ExprKind::Lit(literal) = length_expr.kind + && let LitKind::Int(Pu128(0), _) = literal.node { inner_check(cx, expr, inner_expr, false); } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 6e53ff3ee6e..f0c64fdd573 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.81" +version = "0.1.82" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 6c6a237a8b1..28178a61a93 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { match *arg { GenericArg::Lifetime(l) => self.hash_lifetime(l), GenericArg::Type(ty) => self.hash_ty(ty), - GenericArg::Const(ref ca) => self.hash_const_arg(ca), + GenericArg::Const(ca) => self.hash_const_arg(ca), GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 8c33c34fa1c..3a9714c49ae 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -90,13 +90,14 @@ use std::hash::BuildHasherDefault; use std::iter::{once, repeat}; use std::sync::{Mutex, MutexGuard, OnceLock}; +use clippy_config::types::DisallowedPath; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalModDefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -678,7 +679,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. /// /// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { +pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> { fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ { tcx.crates(()) .iter() @@ -687,8 +688,6 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { .map(CrateNum::as_def_id) } - let tcx = cx.tcx; - let (base, mut path) = match *path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; @@ -739,16 +738,28 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { } /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> { - def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id()) +pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> { + def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) +} + +/// Creates a map of disallowed items to the reason they were disallowed. +pub fn create_disallowed_map( + tcx: TyCtxt<'_>, + disallowed: &'static [DisallowedPath], +) -> DefIdMap<(&'static str, Option<&'static str>)> { + disallowed + .iter() + .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason())) + .flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason)))) + .collect() } /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. /// /// This function is expensive and should be used sparingly. -pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> { - def_path_res(cx, path).into_iter().find_map(|res| match res { +pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> { + def_path_res(tcx, path).into_iter().find_map(|res| match res { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, }) @@ -1411,7 +1422,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e), _ => (), }, - Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) => (), + Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (), _ => break, } } diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index bbe4149fe2a..c5a34160e3d 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -164,6 +164,8 @@ impl<'a> NumericLiteral<'a> { if !exponent.is_empty() && exponent != "0" { output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); + } else if exponent == "0" && self.fraction.is_none() && self.suffix.is_none() { + output.push_str(".0"); } } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 6319c7bfa6b..0f86d89c980 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -52,7 +52,8 @@ impl Display for Sugg<'_> { impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> { - let get_snippet = |span| snippet(cx, span, ""); + let ctxt = expr.span.ctxt(); + let get_snippet = |span| snippet_with_context(cx, span, ctxt, "", &mut Applicability::Unspecified).0; snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet)) } @@ -100,7 +101,9 @@ impl<'a> Sugg<'a> { applicability: &mut Applicability, ) -> Self { if expr.span.ctxt() == ctxt { - Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) + Self::hir_from_snippet(expr, |span| { + snippet_with_context(cx, span, ctxt, default, applicability).0 + }) } else { let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) @@ -109,7 +112,7 @@ 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(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self { + fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { if let Some(range) = higher::Range::hir(expr) { let op = match range.limits { ast::RangeLimits::HalfOpen => AssocOp::DotDot, diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index fc02b974ee1..812fb647fda 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -1182,12 +1182,12 @@ pub struct InteriorMut<'tcx> { } impl<'tcx> InteriorMut<'tcx> { - pub fn new(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { let ignored_def_ids = ignore_interior_mutability .iter() .flat_map(|ignored_ty| { let path: Vec<&str> = ignored_ty.split("::").collect(); - def_path_def_ids(cx, path.as_slice()) + def_path_def_ids(tcx, path.as_slice()) }) .collect(); @@ -1197,10 +1197,10 @@ impl<'tcx> InteriorMut<'tcx> { } } - pub fn without_pointers(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self { + pub fn without_pointers(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { Self { ignore_pointers: true, - ..Self::new(cx, ignore_interior_mutability) + ..Self::new(tcx, ignore_interior_mutability) } } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index cba61c841ef..875ddec259e 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -281,7 +281,7 @@ fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: & { let mut def_path = cx.get_def_path(def_id); def_path.push(path_segment.ident.name); - let reses = def_path_res(cx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>()); + let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>()); if let [res] = reses.as_slice() { Some(*res) } else { None } } else { None diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 3a39e178515..7066c9ad2b9 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -109,23 +109,36 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( res: Option<B>, } impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> { - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + type Result = ControlFlow<()>; + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> ControlFlow<()> { if self.res.is_some() { - return; + return ControlFlow::Break(()); } match (self.f)(e) { ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), - ControlFlow::Break(b) => self.res = Some(b), - ControlFlow::Continue(_) => (), + ControlFlow::Break(b) => { + self.res = Some(b); + ControlFlow::Break(()) + }, + ControlFlow::Continue(_) => ControlFlow::Continue(()), } } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> ControlFlow<()> { + ControlFlow::Continue(()) + } + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> ControlFlow<()> { + ControlFlow::Continue(()) + } + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> ControlFlow<()> { + ControlFlow::Continue(()) + } // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) {} + fn visit_nested_item(&mut self, _: ItemId) -> ControlFlow<()> { + ControlFlow::Continue(()) + } } let mut v = V { f, res: None }; node.visit(&mut v); diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 86d945c14a5..80106f683c2 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.81" +version = "0.1.82" edition = "2021" publish = false diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index 3c86dfe324f..350418eeeb8 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -16,7 +16,7 @@ clap = { version = "4.4", features = ["derive", "env"] } crossbeam-channel = "0.5.6" diff = "0.1.13" flate2 = "1.0" -itertools = "0.12" +itertools = "0.13" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" diff --git a/src/tools/clippy/lintcheck/ci_crates.toml b/src/tools/clippy/lintcheck/ci_crates.toml new file mode 100644 index 00000000000..9e3dbef6a9e --- /dev/null +++ b/src/tools/clippy/lintcheck/ci_crates.toml @@ -0,0 +1,208 @@ +[crates] +# Binaries projects +cargo = {name = "cargo", version = '0.64.0', online_link = 'https://docs.rs/cargo/{version}/src/{file}.html#{line}'} +ripgrep = {name = "ripgrep", version = '14.1.0'} +bat = {name = "bat", version = '0.24.0'} +fend = {name = "fend", version = '1.5.0'} +mdbook = {name = "mdbook", version = '0.4.40'} + +# Bigger crates from ICE issues: +wasmi = {name = "wasmi", version = '0.35.0'} +wgpu = {name = "wgpu", version = '0.20.1'} +bytes = {name = "bytes", version = '1.6.1'} +skrifa = {name = "skrifa", version = '0.19.3'} + +# Random crates which are part of the default test set +puffin = {name = "puffin", version = '0.19.0'} + +# Top ~200 crates from crates.io +syn = { name = 'syn', version = '2.0.71' } +bitflags = { name = 'bitflags', version = '2.6.0' } +hashbrown = { name = 'hashbrown', version = '0.14.5' } +base64 = { name = 'base64', version = '0.22.1' } +regex-syntax = { name = 'regex-syntax', version = '0.8.4' } +proc-macro2 = { name = 'proc-macro2', version = '1.0.86' } +indexmap = { name = 'indexmap', version = '2.2.6' } +quote = { name = 'quote', version = '1.0.36' } +regex-automata = { name = 'regex-automata', version = '0.4.7' } +libc = { name = 'libc', version = '0.2.155' } +serde = { name = 'serde', version = '1.0.204' } +itertools = { name = 'itertools', version = '0.13.0' } +heck = { name = 'heck', version = '0.5.0' } +memchr = { name = 'memchr', version = '2.7.4' } +serde_derive = { name = 'serde_derive', version = '1.0.204' } +unicode-ident = { name = 'unicode-ident', version = '1.0.12' } +autocfg = { name = 'autocfg', version = '1.3.0' } +cfg-if = { name = 'cfg-if', version = '1.0.0' } +aho-corasick = { name = 'aho-corasick', version = '1.1.3' } +getrandom = { name = 'getrandom', version = '0.2.15' } +rand_core = { name = 'rand_core', version = '0.6.4' } +serde_json = { name = 'serde_json', version = '1.0.120' } +itoa = { name = 'itoa', version = '1.0.11' } +rand = { name = 'rand', version = '0.8.5' } +ryu = { name = 'ryu', version = '1.0.18' } +once_cell = { name = 'once_cell', version = '1.19.0' } +rustix = { name = 'rustix', version = '0.38.34' } +regex = { name = 'regex', version = '1.10.5' } +log = { name = 'log', version = '0.4.22' } +parking_lot_core = { name = 'parking_lot_core', version = '0.9.10' } +cc = { name = 'cc', version = '1.1.5' } +strsim = { name = 'strsim', version = '0.11.1' } +clap = { name = 'clap', version = '4.5.9' } +parking_lot = { name = 'parking_lot', version = '0.12.3' } +smallvec = { name = 'smallvec', version = '2.0.0-alpha.6' } +thiserror-impl = { name = 'thiserror-impl', version = '1.0.63' } +thiserror = { name = 'thiserror', version = '1.0.63' } +linux-raw-sys = { name = 'linux-raw-sys', version = '0.6.4' } +socket2 = { name = 'socket2', version = '0.5.7' } +idna = { name = 'idna', version = '1.0.2' } +fastrand = { name = 'fastrand', version = '2.1.0' } +either = { name = 'either', version = '1.13.0' } +num-traits = { name = 'num-traits', version = '0.2.19' } +rand_chacha = { name = 'rand_chacha', version = '0.3.1' } +lazy_static = { name = 'lazy_static', version = '1.5.0' } +semver = { name = 'semver', version = '1.0.23' } +lock_api = { name = 'lock_api', version = '0.4.12' } +scopeguard = { name = 'scopeguard', version = '1.2.0' } +ahash = { name = 'ahash', version = '0.8.11' } +anyhow = { name = 'anyhow', version = '1.0.86' } +rustls = { name = 'rustls', version = '0.23.11' } +http = { name = 'http', version = '1.1.0' } +toml_edit = { name = 'toml_edit', version = '0.22.16' } +pin-project-lite = { name = 'pin-project-lite', version = '0.2.14' } +spin = { name = 'spin', version = '0.9.8' } +miniz_oxide = { name = 'miniz_oxide', version = '0.7.4' } +memoffset = { name = 'memoffset', version = '0.9.1' } +digest = { name = 'digest', version = '0.11.0-pre.8' } +version_check = { name = 'version_check', version = '0.9.4' } +clap_lex = { name = 'clap_lex', version = '0.7.1' } +crossbeam-utils = { name = 'crossbeam-utils', version = '0.8.20' } +toml = { name = 'toml', version = '0.8.15' } +block-buffer = { name = 'block-buffer', version = '0.10.4' } +time = { name = 'time', version = '0.3.36' } +hyper = { name = 'hyper', version = '1.4.1' } +url = { name = 'url', version = '2.5.2' } +percent-encoding = { name = 'percent-encoding', version = '2.3.1' } +tokio = { name = 'tokio', version = '1.38.1' } +errno = { name = 'errno', version = '0.3.9' } +uuid = { name = 'uuid', version = '1.10.0' } +unicode-normalization = { name = 'unicode-normalization', version = '0.1.23' } +ppv-lite86 = { name = 'ppv-lite86', version = '0.2.17' } +futures-core = { name = 'futures-core', version = '0.3.30' } +http-body = { name = 'http-body', version = '1.0.1' } +tinyvec = { name = 'tinyvec', version = '1.8.0' } +futures-util = { name = 'futures-util', version = '0.3.30' } +futures-task = { name = 'futures-task', version = '0.3.30' } +sha2 = { name = 'sha2', version = '0.11.0-pre.3' } +ring = { name = 'ring', version = '0.17.8' } +slab = { name = 'slab', version = '0.4.9' } +chrono = { name = 'chrono', version = '0.4.38' } +futures-sink = { name = 'futures-sink', version = '0.3.30' } +futures-channel = { name = 'futures-channel', version = '0.3.30' } +num_cpus = { name = 'num_cpus', version = '1.16.0' } +untrusted = { name = 'untrusted', version = '0.9.0' } +tinyvec_macros = { name = 'tinyvec_macros', version = '0.1.1' } +mio = { name = 'mio', version = '1.0.0' } +byteorder = { name = 'byteorder', version = '1.5.0' } +form_urlencoded = { name = 'form_urlencoded', version = '1.2.1' } +unicode-bidi = { name = 'unicode-bidi', version = '0.3.15' } +futures-io = { name = 'futures-io', version = '0.3.30' } +tokio-util = { name = 'tokio-util', version = '0.7.11' } +rustls-pemfile = { name = 'rustls-pemfile', version = '2.1.2' } +generic-array = { name = 'generic-array', version = '1.1.0' } +tracing = { name = 'tracing', version = '0.1.40' } +equivalent = { name = 'equivalent', version = '1.0.1' } +tracing-core = { name = 'tracing-core', version = '0.1.32' } +pin-utils = { name = 'pin-utils', version = '0.1.0' } +tempfile = { name = 'tempfile', version = '3.10.1' } +h2 = { name = 'h2', version = '0.4.5' } +futures = { name = 'futures', version = '0.3.30' } +typenum = { name = 'typenum', version = '1.17.0' } +winnow = { name = 'winnow', version = '0.6.13' } +cpufeatures = { name = 'cpufeatures', version = '0.2.12' } +nix = { name = 'nix', version = '0.29.0' } +fnv = { name = 'fnv', version = '1.0.7' } +tokio-rustls = { name = 'tokio-rustls', version = '0.26.0' } +iana-time-zone = { name = 'iana-time-zone', version = '0.1.60' } +rustls-webpki = { name = 'rustls-webpki', version = '0.102.5' } +crc32fast = { name = 'crc32fast', version = '1.4.2' } +adler = { name = 'adler', version = '1.0.2' } +pkg-config = { name = 'pkg-config', version = '0.3.30' } +redox_syscall = { name = 'redox_syscall', version = '0.5.3' } +nom = { name = 'nom', version = '8.0.0-alpha2' } +rustc_version = { name = 'rustc_version', version = '0.4.0' } +futures-macro = { name = 'futures-macro', version = '0.3.30' } +clap_derive = { name = 'clap_derive', version = '4.5.8' } +futures-executor = { name = 'futures-executor', version = '0.3.30' } +event-listener = { name = 'event-listener', version = '5.3.1' } +num-integer = { name = 'num-integer', version = '0.1.46' } +time-macros = { name = 'time-macros', version = '0.2.18' } +flate2 = { name = 'flate2', version = '1.0.30' } +tokio-macros = { name = 'tokio-macros', version = '2.3.0' } +strum_macros = { name = 'strum_macros', version = '0.26.4' } +tracing-attributes = { name = 'tracing-attributes', version = '0.1.27' } +async-trait = { name = 'async-trait', version = '0.1.81' } +crypto-common = { name = 'crypto-common', version = '0.1.6' } +unicode-width = { name = 'unicode-width', version = '0.1.13' } +anstyle = { name = 'anstyle', version = '1.0.7' } +object = { name = 'object', version = '0.36.1' } +gimli = { name = 'gimli', version = '0.31.0' } +crossbeam-epoch = { name = 'crossbeam-epoch', version = '0.9.18' } +thread_local = { name = 'thread_local', version = '1.1.8' } +strum = { name = 'strum', version = '0.26.3' } +darling_core = { name = 'darling_core', version = '0.20.10' } +darling_macro = { name = 'darling_macro', version = '0.20.10' } +minimal-lexical = { name = 'minimal-lexical', version = '0.2.1' } +clap_builder = { name = 'clap_builder', version = '4.5.9' } +time-core = { name = 'time-core', version = '0.1.2' } +httparse = { name = 'httparse', version = '1.9.4' } +signal-hook-registry = { name = 'signal-hook-registry', version = '1.4.2' } +hex = { name = 'hex', version = '0.4.3' } +crossbeam-deque = { name = 'crossbeam-deque', version = '0.8.5' } +zerocopy = { name = 'zerocopy', version = '0.7.35' } +rustversion = { name = 'rustversion', version = '1.0.17' } +env_logger = { name = 'env_logger', version = '0.11.3' } +webpki-roots = { name = 'webpki-roots', version = '0.26.3' } +rustc-demangle = { name = 'rustc-demangle', version = '0.1.24' } +mime = { name = 'mime', version = '0.3.17' } +termcolor = { name = 'termcolor', version = '1.4.1' } +subtle = { name = 'subtle', version = '2.6.1' } +walkdir = { name = 'walkdir', version = '2.5.0' } +hermit-abi = { name = 'hermit-abi', version = '0.4.0' } +pin-project = { name = 'pin-project', version = '1.1.5' } +pin-project-internal = { name = 'pin-project-internal', version = '1.1.5' } +try-lock = { name = 'try-lock', version = '0.2.5' } +tracing-log = { name = 'tracing-log', version = '0.2.0' } +httpdate = { name = 'httpdate', version = '1.0.3' } +anstream = { name = 'anstream', version = '0.6.14' } +crossbeam-channel = { name = 'crossbeam-channel', version = '0.5.13' } +reqwest = { name = 'reqwest', version = '0.12.5' } +want = { name = 'want', version = '0.3.1' } +paste = { name = 'paste', version = '1.0.15' } +anstyle-parse = { name = 'anstyle-parse', version = '0.2.4' } +toml_datetime = { name = 'toml_datetime', version = '0.6.6' } +anstyle-query = { name = 'anstyle-query', version = '1.1.0' } +addr2line = { name = 'addr2line', version = '0.24.0' } +glob = { name = 'glob', version = '0.3.1' } +num-bigint = { name = 'num-bigint', version = '0.4.6' } +backtrace = { name = 'backtrace', version = '0.3.73' } +wasi = { name = 'wasi', version = '0.13.1+wasi-0.2.0' } +tower-service = { name = 'tower-service', version = '0.3.2' } +sync_wrapper = { name = 'sync_wrapper', version = '1.0.1' } +libloading = { name = 'libloading', version = '0.8.4' } +rayon = { name = 'rayon', version = '1.10.0' } +colorchoice = { name = 'colorchoice', version = '1.0.1' } +encoding_rs = { name = 'encoding_rs', version = '0.8.34' } +deranged = { name = 'deranged', version = '0.3.11' } +zeroize = { name = 'zeroize', version = '1.8.1' } +utf8parse = { name = 'utf8parse', version = '0.2.2' } +tracing-subscriber = { name = 'tracing-subscriber', version = '0.3.18' } +hyper-rustls = { name = 'hyper-rustls', version = '0.27.2' } +hmac = { name = 'hmac', version = '0.13.0-pre.3' } +rayon-core = { name = 'rayon-core', version = '1.12.1' } +same-file = { name = 'same-file', version = '1.0.6' } +prost = { name = 'prost', version = '0.13.1' } +sharded-slab = { name = 'sharded-slab', version = '0.1.7' } +textwrap = { name = 'textwrap', version = '0.16.1' } +bumpalo = {name = "bumpalo", version = '3.16.0'} +arrayvec = { name = 'arrayvec', version = '0.7.4' } diff --git a/src/tools/clippy/lintcheck/lintcheck_crates.toml b/src/tools/clippy/lintcheck/lintcheck_crates.toml index ff608e6f935..d205c93c636 100644 --- a/src/tools/clippy/lintcheck/lintcheck_crates.toml +++ b/src/tools/clippy/lintcheck/lintcheck_crates.toml @@ -1,38 +1,44 @@ +# If you want to check a local project it's usually easier to use: +# ``` +# cargo dev lint <path> +# ``` +# +# For testing you can also add sources to git and local repos like this: +# ``` +# crate = {name = "crate", git_url = "https://github.com/name/repo.git", git_hash = "coo1cafe"} +# crate = {name = "crate", path = "/path/to/project"} +# ``` + [crates] -# some of these are from cargotest -cargo = {name = "cargo", version = '0.64.0'} -iron = {name = "iron", version = '0.6.1'} -ripgrep = {name = "ripgrep", version = '12.1.1'} -xsv = {name = "xsv", version = '0.13.0'} -# commented out because of 173K clippy::match_same_arms msgs in language_type.rs -#tokei = { name = "tokei", version = '12.0.4'} -rayon = {name = "rayon", version = '1.5.0'} -serde = {name = "serde", version = '1.0.118'} -# top 10 crates.io dls -bitflags = {name = "bitflags", version = '1.2.1'} -# crash = {name = "clippy_crash", path = "/tmp/clippy_crash"} -libc = {name = "libc", version = '0.2.81'} -log = {name = "log", version = '0.4.11'} -proc-macro2 = {name = "proc-macro2", version = '1.0.24'} -quote = {name = "quote", version = '1.0.7'} -rand = {name = "rand", version = '0.7.3'} -rand_core = {name = "rand_core", version = '0.6.0'} -regex = {name = "regex", version = '1.3.2'} -syn = {name = "syn", version = '1.0.54'} -unicode-xid = {name = "unicode-xid", version = '0.2.1'} -# some more of dtolnays crates -anyhow = {name = "anyhow", version = '1.0.38'} -async-trait = {name = "async-trait", version = '0.1.42'} -cxx = {name = "cxx", version = '1.0.32'} -ryu = {name = "ryu", version = '1.0.5'} -serde_yaml = {name = "serde_yaml", version = '0.8.17'} -thiserror = {name = "thiserror", version = '1.0.24'} -# some embark crates, there are other interesting crates but -# unfortunately adding them increases lintcheck runtime drastically -cfg-expr = {name = "cfg-expr", version = '0.7.1'} -puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} -rpmalloc = {name = "rpmalloc", version = '0.2.0'} -tame-oidc = {name = "tame-oidc", version = '0.1.0'} + +# Some binaries +cargo = {name = "cargo", version = '0.80.0', online_link = 'https://docs.rs/cargo/{version}/src/{file}.html#{line}'} +ripgrep = {name = "ripgrep", version = '14.1.0'} +mdbook = {name = "mdbook", version = '0.4.40'} + +# Common libraries +rayon = {name = "rayon", version = '1.10.0'} +serde = {name = "serde", version = '1.0.204'} +bitflags = {name = "bitflags", version = '2.6.0'} +log = {name = "log", version = '0.4.22'} +quote = {name = "quote", version = '1.0.36'} +proc-macro2 = {name = "proc-macro2", version = '1.0.86'} +rand = {name = "rand", version = '0.8.5'} +rand_core = {name = "rand_core", version = '0.6.4'} +regex = {name = "regex", version = '1.10.5'} +syn = {name = "syn", version = '2.0.71'} +anyhow = {name = "anyhow", version = '1.0.86'} +async-trait = { name = 'async-trait', version = '0.1.81' } +cxx = {name = "cxx", version = '1.0.124'} +ryu = {name = "ryu", version = '1.0.18'} +thiserror = {name = "thiserror", version = '1.0.63'} +serde_yaml = {name = "serde_yaml", version = '0.9.33'} +puffin = {name = "puffin", version = '0.19.0'} +bumpalo = {name = "bumpalo", version = '3.16.0'} +wasmi = {name = "wasmi", version = '0.35.0'} +base64 = { name = 'base64', version = '0.22.1' } +once_cell = { name = 'once_cell', version = '1.19.0' } +tokio = { name = 'tokio', version = '1.38.1' } [recursive] ignore = [ diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index b35a62eed44..6bec1753fc7 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -53,7 +53,13 @@ pub(crate) struct LintcheckConfig { #[derive(Subcommand, Clone, Debug)] pub(crate) enum Commands { /// Display a markdown diff between two lintcheck log files in JSON format - Diff { old: PathBuf, new: PathBuf }, + Diff { + old: PathBuf, + new: PathBuf, + /// This will limit the number of warnings that will be printed for each lint + #[clap(long)] + truncate: bool, + }, /// Create a lintcheck crates TOML file containing the top N popular crates Popular { /// Output TOML file name diff --git a/src/tools/clippy/lintcheck/src/driver.rs b/src/tools/clippy/lintcheck/src/driver.rs index 041be5081f2..2fda2b00f87 100644 --- a/src/tools/clippy/lintcheck/src/driver.rs +++ b/src/tools/clippy/lintcheck/src/driver.rs @@ -11,6 +11,7 @@ use std::{env, mem}; fn run_clippy(addr: &str) -> Option<i32> { let driver_info = DriverInfo { package_name: env::var("CARGO_PKG_NAME").ok()?, + version: env::var("CARGO_PKG_VERSION").ok()?, }; let mut stream = BufReader::new(TcpStream::connect(addr).unwrap()); diff --git a/src/tools/clippy/lintcheck/src/input.rs b/src/tools/clippy/lintcheck/src/input.rs index 3d034391c28..3b263674aa8 100644 --- a/src/tools/clippy/lintcheck/src/input.rs +++ b/src/tools/clippy/lintcheck/src/input.rs @@ -10,6 +10,10 @@ use walkdir::{DirEntry, WalkDir}; use crate::{Crate, LINTCHECK_DOWNLOADS, LINTCHECK_SOURCES}; +const DEFAULT_DOCS_LINK: &str = "https://docs.rs/{krate}/{version}/src/{krate_}/{file}.html#{line}"; +const DEFAULT_GITHUB_LINK: &str = "{url}/blob/{hash}/src/{file}#L{line}"; +const DEFAULT_PATH_LINK: &str = "{path}/src/{file}:{line}"; + /// List of sources to check, loaded from a .toml file #[derive(Debug, Deserialize)] pub struct SourceList { @@ -33,32 +37,62 @@ struct TomlCrate { git_hash: Option<String>, path: Option<String>, options: Option<Vec<String>>, + /// Magic values: + /// * `{krate}` will be replaced by `self.name` + /// * `{krate_}` will be replaced by `self.name` with all `-` replaced by `_` + /// * `{version}` will be replaced by `self.version` + /// * `{url}` will be replaced with `self.git_url` + /// * `{hash}` will be replaced with `self.git_hash` + /// * `{path}` will be replaced with `self.path` + /// * `{file}` will be replaced by the path after `src/` + /// * `{line}` will be replaced by the line + /// + /// If unset, this will be filled by [`read_crates`] since it depends on + /// the source. + online_link: Option<String>, +} + +impl TomlCrate { + fn file_link(&self, default: &str) -> String { + let mut link = self.online_link.clone().unwrap_or_else(|| default.to_string()); + link = link.replace("{krate}", &self.name); + link = link.replace("{krate_}", &self.name.replace('-', "_")); + + if let Some(version) = &self.version { + link = link.replace("{version}", version); + } + if let Some(url) = &self.git_url { + link = link.replace("{url}", url); + } + if let Some(hash) = &self.git_hash { + link = link.replace("{hash}", hash); + } + if let Some(path) = &self.path { + link = link.replace("{path}", path); + } + link + } } /// Represents an archive we download from crates.io, or a git repo, or a local repo/folder /// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct CrateWithSource { + pub name: String, + pub source: CrateSource, + pub file_link: String, + pub options: Option<Vec<String>>, +} + +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] pub enum CrateSource { - CratesIo { - name: String, - version: String, - options: Option<Vec<String>>, - }, - Git { - name: String, - url: String, - commit: String, - options: Option<Vec<String>>, - }, - Path { - name: String, - path: PathBuf, - options: Option<Vec<String>>, - }, + CratesIo { version: String }, + Git { url: String, commit: String }, + Path { path: PathBuf }, } /// Read a `lintcheck_crates.toml` file -pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) { +pub fn read_crates(toml_path: &Path) -> (Vec<CrateWithSource>, RecursiveOptions) { let toml_content: String = fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = @@ -71,23 +105,32 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) { let mut crate_sources = Vec::new(); for tk in tomlcrates { if let Some(ref path) = tk.path { - crate_sources.push(CrateSource::Path { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - path: PathBuf::from(path), + source: CrateSource::Path { + path: PathBuf::from(path), + }, + file_link: tk.file_link(DEFAULT_PATH_LINK), options: tk.options.clone(), }); } else if let Some(ref version) = tk.version { - crate_sources.push(CrateSource::CratesIo { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - version: version.to_string(), + source: CrateSource::CratesIo { + version: version.to_string(), + }, + file_link: tk.file_link(DEFAULT_DOCS_LINK), options: tk.options.clone(), }); } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source - crate_sources.push(CrateSource::Git { + crate_sources.push(CrateWithSource { name: tk.name.clone(), - url: tk.git_url.clone().unwrap(), - commit: tk.git_hash.clone().unwrap(), + source: CrateSource::Git { + url: tk.git_url.clone().unwrap(), + commit: tk.git_hash.clone().unwrap(), + }, + file_link: tk.file_link(DEFAULT_GITHUB_LINK), options: tk.options.clone(), }); } else { @@ -117,12 +160,26 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) { (crate_sources, crate_list.recursive) } -impl CrateSource { +impl CrateWithSource { + pub fn download_and_prepare(&self) -> Crate { + let krate = self.download_and_extract(); + + // Downloaded crates might contain a `rust-toolchain` file. This file + // seems to be accessed when `build.rs` files are present. This access + // results in build errors since lintcheck and clippy will most certainly + // use a different toolchain. + // Lintcheck simply removes these files and assumes that our toolchain + // is more up to date. + let _ = fs::remove_file(krate.path.join("rust-toolchain")); + let _ = fs::remove_file(krate.path.join("rust-toolchain.toml")); + + krate + } /// Makes the sources available on the disk for clippy to check. /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or /// copies a local folder #[expect(clippy::too_many_lines)] - pub fn download_and_extract(&self) -> Crate { + fn download_and_extract(&self) -> Crate { #[allow(clippy::result_large_err)] fn get(path: &str) -> Result<ureq::Response, ureq::Error> { const MAX_RETRIES: u8 = 4; @@ -139,8 +196,11 @@ impl CrateSource { retries += 1; } } - match self { - CrateSource::CratesIo { name, version, options } => { + let name = &self.name; + let options = &self.options; + let file_link = &self.file_link; + match &self.source { + CrateSource::CratesIo { version } => { let extract_dir = PathBuf::from(LINTCHECK_SOURCES); let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); @@ -171,14 +231,10 @@ impl CrateSource { name: name.clone(), path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), + base_url: file_link.clone(), } }, - CrateSource::Git { - name, - url, - commit, - options, - } => { + CrateSource::Git { url, commit } => { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo @@ -217,9 +273,10 @@ impl CrateSource { name: name.clone(), path: repo_path, options: options.clone(), + base_url: file_link.clone(), } }, - CrateSource::Path { name, path, options } => { + CrateSource::Path { path } => { fn is_cache_dir(entry: &DirEntry) -> bool { fs::read(entry.path().join("CACHEDIR.TAG")) .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) @@ -256,6 +313,7 @@ impl CrateSource { name: name.clone(), path: dest_crate_root, options: options.clone(), + base_url: file_link.clone(), } }, } diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs index 1a652927988..4211bce90b2 100644 --- a/src/tools/clippy/lintcheck/src/json.rs +++ b/src/tools/clippy/lintcheck/src/json.rs @@ -1,22 +1,32 @@ use std::fs; use std::path::Path; -use itertools::EitherOrBoth; +use itertools::{EitherOrBoth, Itertools}; use serde::{Deserialize, Serialize}; use crate::ClippyWarning; -#[derive(Deserialize, Serialize)] +/// This is the total number. 300 warnings results in 100 messages per section. +const DEFAULT_LIMIT_PER_LINT: usize = 300; +const TRUNCATION_TOTAL_TARGET: usize = 1000; + +#[derive(Debug, Deserialize, Serialize)] struct LintJson { lint: String, + krate: String, file_name: String, byte_pos: (u32, u32), + file_link: String, rendered: String, } impl LintJson { fn key(&self) -> impl Ord + '_ { - (self.file_name.as_str(), self.byte_pos, self.lint.as_str()) + (self.lint.as_str(), self.file_name.as_str(), self.byte_pos) + } + + fn info_text(&self, action: &str) -> String { + format!("{action} `{}` in `{}` at {}", self.lint, self.krate, self.file_link) } } @@ -29,6 +39,8 @@ pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String { LintJson { file_name: span.file_name.clone(), byte_pos: (span.byte_start, span.byte_end), + krate: warning.krate, + file_link: warning.url, lint: warning.lint, rendered: warning.diag.rendered.unwrap(), } @@ -44,28 +56,142 @@ fn load_warnings(path: &Path) -> Vec<LintJson> { serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) } -fn print_warnings(title: &str, warnings: &[LintJson]) { +pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { + let old_warnings = load_warnings(old_path); + let new_warnings = load_warnings(new_path); + + let mut lint_warnings = vec![]; + + for (name, changes) in &itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) + .chunk_by(|change| change.as_ref().into_left().lint.to_string()) + { + let mut added = Vec::new(); + let mut removed = Vec::new(); + let mut changed = Vec::new(); + for change in changes { + match change { + EitherOrBoth::Both(old, new) => { + if old.rendered != new.rendered { + changed.push((old, new)); + } + }, + EitherOrBoth::Left(old) => removed.push(old), + EitherOrBoth::Right(new) => added.push(new), + } + } + + if !added.is_empty() || !removed.is_empty() || !changed.is_empty() { + lint_warnings.push(LintWarnings { + name, + added, + removed, + changed, + }); + } + } + + print_summary_table(&lint_warnings); + println!(); + + if lint_warnings.is_empty() { + return; + } + + let truncate_after = if truncate { + // Max 15 ensures that we at least have five messages per lint + DEFAULT_LIMIT_PER_LINT + .min(TRUNCATION_TOTAL_TARGET / lint_warnings.len()) + .max(15) + } else { + // No lint should ever each this number of lint emissions, so this is equivialent to + // No truncation + usize::MAX + }; + + for lint in lint_warnings { + print_lint_warnings(&lint, truncate_after); + } +} + +#[derive(Debug)] +struct LintWarnings { + name: String, + added: Vec<LintJson>, + removed: Vec<LintJson>, + changed: Vec<(LintJson, LintJson)>, +} + +fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { + let name = &lint.name; + let html_id = to_html_id(name); + + // The additional anchor is added for non GH viewers that don't prefix ID's + println!(r#"## `{name}` <a id="user-content-{html_id}"></a>"#); + println!(); + + print!( + r##"{}, {}, {}"##, + count_string(name, "added", lint.added.len()), + count_string(name, "removed", lint.removed.len()), + count_string(name, "changed", lint.changed.len()), + ); + println!(); + + print_warnings("Added", &lint.added, truncate_after / 3); + print_warnings("Removed", &lint.removed, truncate_after / 3); + print_changed_diff(&lint.changed, truncate_after / 3); +} + +fn print_summary_table(lints: &[LintWarnings]) { + println!("| Lint | Added | Removed | Changed |"); + println!("| ------------------------------------------ | ------: | ------: | ------: |"); + + for lint in lints { + println!( + "| {:<62} | {:>7} | {:>7} | {:>7} |", + format!("[`{}`](#user-content-{})", lint.name, to_html_id(&lint.name)), + lint.added.len(), + lint.removed.len(), + lint.changed.len() + ); + } +} + +fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { return; } - println!("### {title}"); - println!("```"); + print_h3(&warnings[0].lint, title); + println!(); + + let warnings = truncate(warnings, truncate_after); + for warning in warnings { - print!("{}", warning.rendered); + println!("{}", warning.info_text(title)); + println!(); + println!("```"); + println!("{}", warning.rendered.trim_end()); + println!("```"); + println!(); } - println!("```"); } -fn print_changed_diff(changed: &[(LintJson, LintJson)]) { +fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { if changed.is_empty() { return; } - println!("### Changed"); - println!("```diff"); + print_h3(&changed[0].0.lint, "Changed"); + println!(); + + let changed = truncate(changed, truncate_after); + for (old, new) in changed { - for change in diff::lines(&old.rendered, &new.rendered) { + println!("{}", new.info_text("Changed")); + println!(); + println!("```diff"); + for change in diff::lines(old.rendered.trim_end(), new.rendered.trim_end()) { use diff::Result::{Both, Left, Right}; match change { @@ -80,38 +206,48 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)]) { }, } } + println!("```"); } - println!("```"); } -pub(crate) fn diff(old_path: &Path, new_path: &Path) { - let old_warnings = load_warnings(old_path); - let new_warnings = load_warnings(new_path); - - let mut added = Vec::new(); - let mut removed = Vec::new(); - let mut changed = Vec::new(); - - for change in itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) { - match change { - EitherOrBoth::Both(old, new) => { - if old.rendered != new.rendered { - changed.push((old, new)); - } - }, - EitherOrBoth::Left(old) => removed.push(old), - EitherOrBoth::Right(new) => added.push(new), - } +fn truncate<T>(list: &[T], truncate_after: usize) -> &[T] { + if list.len() > truncate_after { + println!( + "{} warnings have been truncated for this summary.", + list.len() - truncate_after + ); + println!(); + + list.split_at(truncate_after).0 + } else { + list } +} - print!( - "{} added, {} removed, {} changed\n\n", - added.len(), - removed.len(), - changed.len() - ); +fn print_h3(lint: &str, title: &str) { + let html_id = to_html_id(lint); + // We have to use HTML here to be able to manually add an id. + println!(r#"### {title} <a id="user-content-{html_id}-{title}"></a>"#); +} - print_warnings("Added", &added); - print_warnings("Removed", &removed); - print_changed_diff(&changed); +/// GitHub's markdown parsers doesn't like IDs with `::` and `_`. This simplifies +/// the lint name for the HTML ID. +fn to_html_id(lint_name: &str) -> String { + lint_name.replace("clippy::", "").replace('_', "-") +} + +/// This generates the `x added` string for the start of the job summery. +/// It linkifies them if possible to jump to the respective heading. +fn count_string(lint: &str, label: &str, count: usize) -> String { + // Headlines are only added, if anything will be displayed under the headline. + // We therefore only want to add links to them if they exist + if count == 0 { + format!("0 {label}") + } else { + let html_id = to_html_id(lint); + // GitHub's job summaries don't add HTML ids to headings. That's why we + // manually have to add them. GitHub prefixes these manual ids with + // `user-content-` and that's how we end up with these awesome links :D + format!("[{count} {label}](#user-content-{html_id}-{label})") + } } diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index e37ffab13ac..0dd62ded293 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -6,6 +6,7 @@ // positives. #![feature(iter_collect_into)] +#![feature(let_chains)] #![warn( trivial_casts, trivial_numeric_casts, @@ -38,7 +39,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::{env, fs}; use cargo_metadata::Message; -use input::{read_crates, CrateSource}; +use input::read_crates; use output::{ClippyCheckOutput, ClippyWarning, RustcIce}; use rayon::prelude::*; @@ -53,6 +54,7 @@ struct Crate { // path to the extracted sources that clippy can check path: PathBuf, options: Option<Vec<String>>, + base_url: String, } impl Crate { @@ -86,8 +88,6 @@ impl Crate { ); } - let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let cargo_home = env!("CARGO_HOME"); // `src/lib.rs` -> `target/lintcheck/sources/crate-1.2.3/src/lib.rs` @@ -131,7 +131,7 @@ impl Crate { // The wrapper is set to `lintcheck` itself so we can force enable linting and ignore certain crates // (see `crate::driver`) let status = cmd - .env("CARGO_TARGET_DIR", shared_target_dir.join("recursive")) + .env("CARGO_TARGET_DIR", shared_target_dir("recursive")) .env("RUSTC_WRAPPER", env::current_exe().unwrap()) // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various // different working directories @@ -149,9 +149,10 @@ impl Crate { cmd.arg("--message-format=json"); } + let shared_target_dir = shared_target_dir(&format!("_{thread_index:?}")); let all_output = cmd // use the looping index to create individual target dirs - .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) + .env("CARGO_TARGET_DIR", shared_target_dir.as_os_str()) // Roughly equivalent to `cargo clippy`/`cargo clippy --fix` .env("RUSTC_WORKSPACE_WRAPPER", clippy_driver_path) .output() @@ -185,7 +186,11 @@ impl Crate { // get all clippy warnings and ICEs let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes()) .filter_map(|msg| match msg { - Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message), + Ok(Message::CompilerMessage(message)) => ClippyWarning::new( + normalize_diag(message.message, shared_target_dir.to_str().unwrap()), + &self.base_url, + &self.name, + ), _ => None, }) .map(ClippyCheckOutput::ClippyWarning) @@ -201,6 +206,31 @@ impl Crate { } } +/// The target directory can sometimes be stored in the file name of spans. +/// This is problematic since the directory in constructed from the thread +/// ID and also used in our CI to determine if two lint emissions are the +/// same or not. This function simply normalizes the `_<thread_id>` to `_*`. +fn normalize_diag( + mut message: cargo_metadata::diagnostic::Diagnostic, + thread_target_dir: &str, +) -> cargo_metadata::diagnostic::Diagnostic { + let mut dir_found = false; + message + .spans + .iter_mut() + .filter(|span| span.file_name.starts_with(thread_target_dir)) + .for_each(|span| { + dir_found = true; + span.file_name + .replace_range(0..thread_target_dir.len(), shared_target_dir("_*").to_str().unwrap()); + }); + + if dir_found && let Some(rendered) = &mut message.rendered { + *rendered = rendered.replace(thread_target_dir, shared_target_dir("_*").to_str().unwrap()); + } + message +} + /// Builds clippy inside the repo to make sure we have a clippy executable we can use. fn build_clippy() -> String { let output = Command::new("cargo") @@ -230,7 +260,7 @@ fn main() { let config = LintcheckConfig::new(); match config.subcommand { - Some(Commands::Diff { old, new }) => json::diff(&old, &new), + Some(Commands::Diff { old, new, truncate }) => json::diff(&old, &new, truncate), Some(Commands::Popular { output, number }) => popular_crates::fetch(output, number).unwrap(), None => lintcheck(config), } @@ -292,18 +322,12 @@ fn lintcheck(config: LintcheckConfig) { .into_iter() .filter(|krate| { if let Some(only_one_crate) = &config.only { - let name = match krate { - CrateSource::CratesIo { name, .. } - | CrateSource::Git { name, .. } - | CrateSource::Path { name, .. } => name, - }; - - name == only_one_crate + krate.name == *only_one_crate } else { true } }) - .map(|krate| krate.download_and_extract()) + .map(|krate| krate.download_and_prepare()) .collect(); if crates.is_empty() { @@ -393,6 +417,15 @@ fn clippy_project_root() -> &'static Path { Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() } +/// The qualifier can be used to separate different threads from another. By +/// default it should be set to `_<thread_id>` +#[must_use] +fn shared_target_dir(qualifier: &str) -> PathBuf { + clippy_project_root() + .join("target/lintcheck/shared_target_dir") + .join(qualifier) +} + #[test] fn lintcheck_test() { let args = [ diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index 4bfc554ef9e..15378630695 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -53,11 +53,13 @@ impl RustcIce { pub struct ClippyWarning { pub lint: String, pub diag: Diagnostic, + pub krate: String, + /// The URL that points to the file and line of the lint emission + pub url: String, } -#[allow(unused)] impl ClippyWarning { - pub fn new(mut diag: Diagnostic) -> Option<Self> { + pub fn new(mut diag: Diagnostic, base_url: &str, krate: &str) -> Option<Self> { let lint = diag.code.clone()?.code; if !(lint.contains("clippy") || diag.message.contains("clippy")) || diag.message.contains("could not read cargo metadata") @@ -69,7 +71,32 @@ impl ClippyWarning { let rendered = diag.rendered.as_mut().unwrap(); *rendered = strip_ansi_escapes::strip_str(&rendered); - Some(Self { lint, diag }) + // Turns out that there are lints without spans... For example Rust's + // `renamed_and_removed_lints` if the lint is given via the CLI. + let span = diag + .spans + .iter() + .find(|span| span.is_primary) + .or(diag.spans.first()) + .unwrap_or_else(|| panic!("Diagnostic without span: {diag}")); + let file = &span.file_name; + let url = if let Some(src_split) = file.find("/src/") { + // This removes the initial `target/lintcheck/sources/<crate>-<version>/` + let src_split = src_split + "/src/".len(); + let (_, file) = file.split_at(src_split); + + let line_no = span.line_start; + base_url.replace("{file}", file).replace("{line}", &line_no.to_string()) + } else { + file.clone() + }; + + Some(Self { + lint, + diag, + url, + krate: krate.to_string(), + }) } pub fn span(&self) -> &DiagnosticSpan { diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs index 373ca6f9918..6a662970ae8 100644 --- a/src/tools/clippy/lintcheck/src/recursive.rs +++ b/src/tools/clippy/lintcheck/src/recursive.rs @@ -20,6 +20,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] pub(crate) struct DriverInfo { pub package_name: String, + pub version: String, } pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W) @@ -61,10 +62,17 @@ fn process_stream( let mut stderr = String::new(); stream.read_to_string(&mut stderr).unwrap(); + // It's 99% likely that dependencies compiled with recursive mode are on crates.io + // and therefore on docs.rs. This links to the sources directly, do avoid invalid + // links due to remaped paths. See rust-lang/docs.rs#2551 for more details. + let base_url = format!( + "https://docs.rs/crate/{}/{}/source/src/{{file}}#{{line}}", + driver_info.package_name, driver_info.version + ); let messages = stderr .lines() .filter_map(|json_msg| serde_json::from_str::<Diagnostic>(json_msg).ok()) - .filter_map(ClippyWarning::new); + .filter_map(|diag| ClippyWarning::new(diag, &base_url, &driver_info.package_name)); for message in messages { sender.send(message).unwrap(); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index a61c22c59f9..69fb11a4824 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-07-11" +channel = "nightly-2024-07-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 333a2ab5857..ea3a0a93ecc 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -5,7 +5,7 @@ use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; +use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; use std::collections::BTreeMap; use std::env::{self, set_var, var_os}; @@ -122,7 +122,8 @@ fn base_config(test_dir: &str) -> (Config, Args) { out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; - config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo)).into(); + config.comment_defaults.base().exit_status = None.into(); + config.comment_defaults.base().require_annotations = None.into(); config .comment_defaults .base() @@ -267,15 +268,6 @@ fn run_ui_cargo() { } fn main() { - // Support being run by cargo nextest - https://nexte.st/book/custom-test-harnesses.html - if env::args().any(|arg| arg == "--list") { - if !env::args().any(|arg| arg == "--ignored") { - println!("compile_test: test"); - } - - return; - } - set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); // The SPEEDTEST_* env variables can be used to check Clippy's performance on your PR. It runs the // affected test 1000 times and gets the average. diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 36a7a651c4d..c8a761bf509 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -7,23 +7,41 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; +use std::fs::File; +use std::io::{self, IsTerminal}; use std::path::PathBuf; use std::process::Command; +use std::time::SystemTime; use test_utils::IS_RUSTC_TEST_SUITE; +use ui_test::Args; mod test_utils; -#[test] -fn dogfood_clippy() { +fn main() { if IS_RUSTC_TEST_SUITE { return; } + let args = Args::test().unwrap(); + + if args.list { + if !args.ignored { + println!("dogfood: test"); + } + } else if !args.skip.iter().any(|arg| arg == "dogfood") { + if args.filters.iter().any(|arg| arg == "collect_metadata") { + collect_metadata(); + } else { + dogfood(); + } + } +} + +fn dogfood() { let mut failed_packages = Vec::new(); - // "" is the root package for package in [ - "", + "./", "clippy_dev", "clippy_lints", "clippy_utils", @@ -31,6 +49,7 @@ fn dogfood_clippy() { "lintcheck", "rustc_tools_util", ] { + println!("linting {package}"); if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { failed_packages.push(if package.is_empty() { "root" } else { package }); } @@ -43,12 +62,8 @@ fn dogfood_clippy() { ); } -#[test] -#[ignore] -#[cfg(feature = "internal")] -fn run_metadata_collection_lint() { - use std::fs::File; - use std::time::SystemTime; +fn collect_metadata() { + assert!(cfg!(feature = "internal")); // Setup for validation let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json"); @@ -101,6 +116,10 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { .arg("--all-targets") .arg("--all-features"); + if !io::stdout().is_terminal() { + command.arg("-q"); + } + if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") { for arg in dogfood_args.split_whitespace() { command.arg(arg); @@ -119,11 +138,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { command.args(["-A", "unknown_lints"]); } - let output = command.output().unwrap(); - - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - output.status.success() + command.status().unwrap().success() } diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index 19c5f3a4133..77b7bb6a7bf 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -29,8 +29,10 @@ fn integration_test() { .nth(1) .expect("repo name should have format `<org>/<name>`"); - let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path(); - repo_dir.push(crate_name); + let repo_dir = tempfile::tempdir() + .expect("couldn't create temp dir") + .into_path() + .join(crate_name); let st = Command::new("git") .args([ diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index 66eda44f745..16e1487f2bb 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml) + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` @@ -14,7 +14,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml) + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 016ee502c24..39b8634be10 100644 --- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -1,26 +1,26 @@ -error: `std::string::String` may not be held across an await point per `clippy.toml` +error: holding a disallowed type across an await point `std::string::String` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9 | LL | let _x = String::from("hello"); | ^^ | - = note: strings are bad (from clippy.toml) + = note: strings are bad = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]` -error: `std::net::Ipv4Addr` may not be held across an await point per `clippy.toml` +error: holding a disallowed type across an await point `std::net::Ipv4Addr` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:10:9 | LL | let x = Ipv4Addr::new(127, 0, 0, 1); | ^ -error: `std::string::String` may not be held across an await point per `clippy.toml` +error: holding a disallowed type across an await point `std::string::String` --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:33:13 | LL | let _x = String::from("hi!"); | ^^ | - = note: strings are bad (from clippy.toml) + = note: strings are bad error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 290cd3d0010..ddeb2f8cc70 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -31,7 +31,7 @@ error: use of a disallowed macro `serde::Serialize` LL | #[derive(Serialize)] | ^^^^^^^^^ | - = note: no serializing (from clippy.toml) + = note: no serializing error: use of a disallowed macro `macros::expr` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:21:13 diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed deleted file mode 100644 index 36540bf1dcf..00000000000 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ /dev/null @@ -1,24 +0,0 @@ -#![deny(clippy::index_refutable_slice)] - -fn below_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { - //~^ ERROR: binding can be a slice pattern - // This would usually not be linted but is included now due to the - // index limit in the config file - println!("{}", slice_7); - } -} - -fn above_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - // This will not be linted as 8 is above the limit - println!("{}", slice[8]); - } -} - -fn main() { - below_limit(); - above_limit(); -} diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd9..e64c8ff3290 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,5 +1,7 @@ #![deny(clippy::index_refutable_slice)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some(slice) = slice { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 0b989e5cf06..3ea600c7d7b 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:7:17 | LL | if let Some(slice) = slice { | ^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f661e76cc74..e77b2b95949 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -13,7 +13,7 @@ error: use of a disallowed method `regex::Regex::is_match` LL | re.is_match("abc"); | ^^^^^^^^ | - = note: no matching allowed (from clippy.toml) + = note: no matching allowed error: use of a disallowed method `std::iter::Iterator::sum` --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 20df8e88d36..322cde15526 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -1,4 +1,4 @@ -error: `std::sync::atomic::AtomicU32` is not allowed according to config +error: use of a disallowed type `std::sync::atomic::AtomicU32` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:7:1 | LL | use std::sync::atomic::AtomicU32; @@ -7,123 +7,123 @@ LL | use std::sync::atomic::AtomicU32; = note: `-D clippy::disallowed-types` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` -error: `std::time::Instant` is not allowed according to config +error: use of a disallowed type `std::time::Instant` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:8:1 | LL | use std::time::Instant as Sneaky; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::time::Instant` is not allowed according to config +error: use of a disallowed type `std::time::Instant` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:12:33 | LL | fn bad_return_type() -> fn() -> Sneaky { | ^^^^^^ -error: `std::time::Instant` is not allowed according to config +error: use of a disallowed type `std::time::Instant` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:16:28 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {} | ^^^^^^ -error: `std::sync::atomic::AtomicU32` is not allowed according to config +error: use of a disallowed type `std::sync::atomic::AtomicU32` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:16:39 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: `std::io::Read` is not allowed according to config +error: use of a disallowed type `std::io::Read` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:18:22 | LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ -error: `usize` is not allowed according to config +error: use of a disallowed type `std::primitive::usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:20:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^^ -error: `bool` is not allowed according to config +error: use of a disallowed type `bool` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:20:43 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ -error: `usize` is not allowed according to config +error: use of a disallowed type `std::primitive::usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:22:28 | LL | fn const_generics<const C: usize>() {} | ^^^^^ -error: `usize` is not allowed according to config +error: use of a disallowed type `std::primitive::usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:24:24 | LL | struct GenArg<const U: usize>([u8; U]); | ^^^^^ -error: `std::net::Ipv4Addr` is not allowed according to config +error: use of a disallowed type `std::net::Ipv4Addr` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:28:10 | LL | fn ip(_: std::net::Ipv4Addr) {} | ^^^^^^^^^^^^^^^^^^ | - = note: no IPv4 allowed (from clippy.toml) + = note: no IPv4 allowed -error: `std::net::TcpListener` is not allowed according to config +error: use of a disallowed type `std::net::TcpListener` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:16 | LL | fn listener(_: std::net::TcpListener) {} | ^^^^^^^^^^^^^^^^^^^^^ -error: `std::collections::HashMap` is not allowed according to config +error: use of a disallowed type `std::collections::HashMap` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:34:48 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::collections::HashMap` is not allowed according to config +error: use of a disallowed type `std::collections::HashMap` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:34:12 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::time::Instant` is not allowed according to config +error: use of a disallowed type `std::time::Instant` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:35:13 | LL | let _ = Sneaky::now(); | ^^^^^^ -error: `std::sync::atomic::AtomicU32` is not allowed according to config +error: use of a disallowed type `std::sync::atomic::AtomicU32` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:36:13 | LL | let _ = foo::atomic::AtomicU32::new(0); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `std::sync::atomic::AtomicU32` is not allowed according to config +error: use of a disallowed type `std::sync::atomic::AtomicU32` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:37:17 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::sync::atomic::AtomicU32` is not allowed according to config +error: use of a disallowed type `std::sync::atomic::AtomicU32` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:37:48 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `syn::TypePath` is not allowed according to config +error: use of a disallowed type `syn::TypePath` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:38:43 | LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); | ^^^^^^^^^^^^^ -error: `syn::Ident` is not allowed according to config +error: use of a disallowed type `proc_macro2::Ident` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:39:13 | LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ -error: `usize` is not allowed according to config +error: use of a disallowed type `std::primitive::usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:41:12 | LL | let _: usize = 64_usize; diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr index b47b35461f6..82d6b2e4b8e 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr @@ -1,95 +1,184 @@ -error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` +error: casts from `bool` to `u8` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:8:13 | LL | let _ = true as u8; - | ^^^^^^^^^^ help: try: `u8::from(true)` + | ^^^^^^^^^^ | + = help: an `as` cast can become silently lossy if the types change in the future = note: `-D clippy::cast-lossless` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` +help: use `u8::from` instead + | +LL | let _ = u8::from(true); + | ~~~~~~~~~~~~~~ -error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` +error: casts from `bool` to `u16` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:9:13 | LL | let _ = true as u16; - | ^^^^^^^^^^^ help: try: `u16::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u16::from` instead + | +LL | let _ = u16::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` +error: casts from `bool` to `u32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:10:13 | LL | let _ = true as u32; - | ^^^^^^^^^^^ help: try: `u32::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u32::from` instead + | +LL | let _ = u32::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` +error: casts from `bool` to `u64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:11:13 | LL | let _ = true as u64; - | ^^^^^^^^^^^ help: try: `u64::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u64::from` instead + | +LL | let _ = u64::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` +error: casts from `bool` to `u128` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:12:13 | LL | let _ = true as u128; - | ^^^^^^^^^^^^ help: try: `u128::from(true)` + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u128::from` instead + | +LL | let _ = u128::from(true); + | ~~~~~~~~~~~~~~~~ -error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` +error: casts from `bool` to `usize` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:13:13 | LL | let _ = true as usize; - | ^^^^^^^^^^^^^ help: try: `usize::from(true)` + | ^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `usize::from` instead + | +LL | let _ = usize::from(true); + | ~~~~~~~~~~~~~~~~~ -error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` +error: casts from `bool` to `i8` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:15:13 | LL | let _ = true as i8; - | ^^^^^^^^^^ help: try: `i8::from(true)` + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i8::from` instead + | +LL | let _ = i8::from(true); + | ~~~~~~~~~~~~~~ -error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` +error: casts from `bool` to `i16` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:16:13 | LL | let _ = true as i16; - | ^^^^^^^^^^^ help: try: `i16::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i16::from` instead + | +LL | let _ = i16::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` +error: casts from `bool` to `i32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:17:13 | LL | let _ = true as i32; - | ^^^^^^^^^^^ help: try: `i32::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead + | +LL | let _ = i32::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` +error: casts from `bool` to `i64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:18:13 | LL | let _ = true as i64; - | ^^^^^^^^^^^ help: try: `i64::from(true)` + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | let _ = i64::from(true); + | ~~~~~~~~~~~~~~~ -error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` +error: casts from `bool` to `i128` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:19:13 | LL | let _ = true as i128; - | ^^^^^^^^^^^^ help: try: `i128::from(true)` + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | let _ = i128::from(true); + | ~~~~~~~~~~~~~~~~ -error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` +error: casts from `bool` to `isize` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:20:13 | LL | let _ = true as isize; - | ^^^^^^^^^^^^^ help: try: `isize::from(true)` + | ^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `isize::from` instead + | +LL | let _ = isize::from(true); + | ~~~~~~~~~~~~~~~~~ -error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` +error: casts from `bool` to `u16` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:23:13 | LL | let _ = (true | false) as u16; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u16::from` instead + | +LL | let _ = u16::from(true | false); + | ~~~~~~~~~~~~~~~~~~~~~~~ -error: casting `bool` to `U8` is more cleanly stated with `U8::from(_)` +error: casts from `bool` to `u8` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:25:13 | LL | let _ = true as U8; - | ^^^^^^^^^^ help: try: `U8::from(true)` + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `U8::from` instead + | +LL | let _ = U8::from(true); + | ~~~~~~~~~~~~~~ -error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` +error: casts from `bool` to `u8` can be expressed infallibly using `From` --> tests/ui/cast_lossless_bool.rs:53:13 | LL | let _ = true as u8; - | ^^^^^^^^^^ help: try: `u8::from(true)` + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u8::from` instead + | +LL | let _ = u8::from(true); + | ~~~~~~~~~~~~~~ error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr index f2ba4e3b990..b36f8bcecf5 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr @@ -1,83 +1,160 @@ -error: casting `i8` to `f32` may become silently lossy if you later change the type +error: casts from `i8` to `f32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:12:13 | LL | let _ = x0 as f32; - | ^^^^^^^^^ help: try: `f32::from(x0)` + | ^^^^^^^^^ | + = help: an `as` cast can become silently lossy if the types change in the future = note: `-D clippy::cast-lossless` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` +help: use `f32::from` instead + | +LL | let _ = f32::from(x0); + | ~~~~~~~~~~~~~ -error: casting `i8` to `f64` may become silently lossy if you later change the type +error: casts from `i8` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:13:13 | LL | let _ = x0 as f64; - | ^^^^^^^^^ help: try: `f64::from(x0)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x0); + | ~~~~~~~~~~~~~ -error: casting `i8` to `F32` may become silently lossy if you later change the type +error: casts from `i8` to `f32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:14:13 | LL | let _ = x0 as F32; - | ^^^^^^^^^ help: try: `F32::from(x0)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `F32::from` instead + | +LL | let _ = F32::from(x0); + | ~~~~~~~~~~~~~ -error: casting `i8` to `F64` may become silently lossy if you later change the type +error: casts from `i8` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:15:13 | LL | let _ = x0 as F64; - | ^^^^^^^^^ help: try: `F64::from(x0)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `F64::from` instead + | +LL | let _ = F64::from(x0); + | ~~~~~~~~~~~~~ -error: casting `u8` to `f32` may become silently lossy if you later change the type +error: casts from `u8` to `f32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:17:13 | LL | let _ = x1 as f32; - | ^^^^^^^^^ help: try: `f32::from(x1)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f32::from` instead + | +LL | let _ = f32::from(x1); + | ~~~~~~~~~~~~~ -error: casting `u8` to `f64` may become silently lossy if you later change the type +error: casts from `u8` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:18:13 | LL | let _ = x1 as f64; - | ^^^^^^^^^ help: try: `f64::from(x1)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x1); + | ~~~~~~~~~~~~~ -error: casting `i16` to `f32` may become silently lossy if you later change the type +error: casts from `i16` to `f32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:20:13 | LL | let _ = x2 as f32; - | ^^^^^^^^^ help: try: `f32::from(x2)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f32::from` instead + | +LL | let _ = f32::from(x2); + | ~~~~~~~~~~~~~ -error: casting `i16` to `f64` may become silently lossy if you later change the type +error: casts from `i16` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:21:13 | LL | let _ = x2 as f64; - | ^^^^^^^^^ help: try: `f64::from(x2)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x2); + | ~~~~~~~~~~~~~ -error: casting `u16` to `f32` may become silently lossy if you later change the type +error: casts from `u16` to `f32` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:23:13 | LL | let _ = x3 as f32; - | ^^^^^^^^^ help: try: `f32::from(x3)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f32::from` instead + | +LL | let _ = f32::from(x3); + | ~~~~~~~~~~~~~ -error: casting `u16` to `f64` may become silently lossy if you later change the type +error: casts from `u16` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:24:13 | LL | let _ = x3 as f64; - | ^^^^^^^^^ help: try: `f64::from(x3)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x3); + | ~~~~~~~~~~~~~ -error: casting `i32` to `f64` may become silently lossy if you later change the type +error: casts from `i32` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:26:13 | LL | let _ = x4 as f64; - | ^^^^^^^^^ help: try: `f64::from(x4)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x4); + | ~~~~~~~~~~~~~ -error: casting `u32` to `f64` may become silently lossy if you later change the type +error: casts from `u32` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:28:13 | LL | let _ = x5 as f64; - | ^^^^^^^^^ help: try: `f64::from(x5)` + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(x5); + | ~~~~~~~~~~~~~ -error: casting `f32` to `f64` may become silently lossy if you later change the type +error: casts from `f32` to `f64` can be expressed infallibly using `From` --> tests/ui/cast_lossless_float.rs:31:13 | LL | let _ = 1.0f32 as f64; - | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` + | ^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `f64::from` instead + | +LL | let _ = f64::from(1.0f32); + | ~~~~~~~~~~~~~~~~~ error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed index 291556a9774..cdb06567836 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed @@ -1,39 +1,93 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] -type I64 = i64; -type U128 = u128; +type I64Alias = i64; fn main() { // Test clippy::cast_lossless with casts to integer types - let _ = i16::from(1i8); - let _ = i32::from(1i8); - let _ = i64::from(1i8); - let _ = i16::from(1u8); - let _ = i32::from(1u8); - let _ = i64::from(1u8); - let _ = u16::from(1u8); - let _ = u32::from(1u8); - let _ = u64::from(1u8); - let _ = i32::from(1i16); - let _ = i64::from(1i16); - let _ = i32::from(1u16); - let _ = i64::from(1u16); - let _ = u32::from(1u16); - let _ = u64::from(1u16); - let _ = i64::from(1i32); - let _ = i64::from(1u32); - let _ = u64::from(1u32); + u16::from(0u8); + //~^ cast_lossless + i16::from(0u8); + //~^ cast_lossless + u32::from(0u8); + //~^ cast_lossless + i32::from(0u8); + //~^ cast_lossless + u64::from(0u8); + //~^ cast_lossless + i64::from(0u8); + //~^ cast_lossless + u128::from(0u8); + //~^ cast_lossless + i128::from(0u8); + //~^ cast_lossless + + u32::from(0u16); + //~^ cast_lossless + i32::from(0u16); + //~^ cast_lossless + u64::from(0u16); + //~^ cast_lossless + i64::from(0u16); + //~^ cast_lossless + u128::from(0u16); + //~^ cast_lossless + i128::from(0u16); + //~^ cast_lossless + + u64::from(0u32); + //~^ cast_lossless + i64::from(0u32); + //~^ cast_lossless + u128::from(0u32); + //~^ cast_lossless + i128::from(0u32); + //~^ cast_lossless + + u128::from(0u64); + //~^ cast_lossless + i128::from(0u64); + //~^ cast_lossless + + i16::from(0i8); + //~^ cast_lossless + i32::from(0i8); + //~^ cast_lossless + i64::from(0i8); + //~^ cast_lossless + i128::from(0i8); + //~^ cast_lossless + + i32::from(0i16); + //~^ cast_lossless + i64::from(0i16); + //~^ cast_lossless + i128::from(0i16); + //~^ cast_lossless + + i64::from(0i32); + //~^ cast_lossless + i128::from(0i32); + //~^ cast_lossless + + i128::from(0i64); + //~^ cast_lossless // Test with an expression wrapped in parens let _ = u16::from(1u8 + 1u8); + //~^ cast_lossless - let _ = I64::from(1i8); + let _ = I64Alias::from(1i8); + //~^ cast_lossless - // Do not lint if destination type is u128 - // see https://github.com/rust-lang/rust-clippy/issues/12492 - let _ = 1u8 as u128; - let _ = 1u8 as U128; + let _: u16 = 0u8.into(); + //~^ cast_lossless + let _: i16 = (-1i8).into(); + //~^ cast_lossless + let _: u16 = (1u8 + 2).into(); + //~^ cast_lossless + let _: u32 = (1i8 as u16).into(); + //~^ cast_lossless } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, @@ -68,5 +122,30 @@ fn issue11458() { } let x = 10_u128; let _ = i32::from(sign_cast!(x, u8, i8)); + //~^ cast_lossless let _ = i32::from(sign_cast!(x, u8, i8) + 1); + //~^ cast_lossless +} + +fn issue12695() { + macro_rules! in_macro { + () => { + u32::from(1u8) + //~^ cast_lossless + }; + } + + let _ = in_macro!(); } + +fn ty_from_macro() { + macro_rules! ty { + () => { + u32 + }; + } + + let _ = <ty!()>::from(0u8); +} + +const IN_CONST: u64 = 0u8 as u64; diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.rs b/src/tools/clippy/tests/ui/cast_lossless_integer.rs index a917c7a371d..1f510b1a303 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.rs +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.rs @@ -1,39 +1,93 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] -type I64 = i64; -type U128 = u128; +type I64Alias = i64; fn main() { // Test clippy::cast_lossless with casts to integer types - let _ = 1i8 as i16; - let _ = 1i8 as i32; - let _ = 1i8 as i64; - let _ = 1u8 as i16; - let _ = 1u8 as i32; - let _ = 1u8 as i64; - let _ = 1u8 as u16; - let _ = 1u8 as u32; - let _ = 1u8 as u64; - let _ = 1i16 as i32; - let _ = 1i16 as i64; - let _ = 1u16 as i32; - let _ = 1u16 as i64; - let _ = 1u16 as u32; - let _ = 1u16 as u64; - let _ = 1i32 as i64; - let _ = 1u32 as i64; - let _ = 1u32 as u64; + 0u8 as u16; + //~^ cast_lossless + 0u8 as i16; + //~^ cast_lossless + 0u8 as u32; + //~^ cast_lossless + 0u8 as i32; + //~^ cast_lossless + 0u8 as u64; + //~^ cast_lossless + 0u8 as i64; + //~^ cast_lossless + 0u8 as u128; + //~^ cast_lossless + 0u8 as i128; + //~^ cast_lossless + + 0u16 as u32; + //~^ cast_lossless + 0u16 as i32; + //~^ cast_lossless + 0u16 as u64; + //~^ cast_lossless + 0u16 as i64; + //~^ cast_lossless + 0u16 as u128; + //~^ cast_lossless + 0u16 as i128; + //~^ cast_lossless + + 0u32 as u64; + //~^ cast_lossless + 0u32 as i64; + //~^ cast_lossless + 0u32 as u128; + //~^ cast_lossless + 0u32 as i128; + //~^ cast_lossless + + 0u64 as u128; + //~^ cast_lossless + 0u64 as i128; + //~^ cast_lossless + + 0i8 as i16; + //~^ cast_lossless + 0i8 as i32; + //~^ cast_lossless + 0i8 as i64; + //~^ cast_lossless + 0i8 as i128; + //~^ cast_lossless + + 0i16 as i32; + //~^ cast_lossless + 0i16 as i64; + //~^ cast_lossless + 0i16 as i128; + //~^ cast_lossless + + 0i32 as i64; + //~^ cast_lossless + 0i32 as i128; + //~^ cast_lossless + + 0i64 as i128; + //~^ cast_lossless // Test with an expression wrapped in parens let _ = (1u8 + 1u8) as u16; + //~^ cast_lossless - let _ = 1i8 as I64; + let _ = 1i8 as I64Alias; + //~^ cast_lossless - // Do not lint if destination type is u128 - // see https://github.com/rust-lang/rust-clippy/issues/12492 - let _ = 1u8 as u128; - let _ = 1u8 as U128; + let _: u16 = 0u8 as _; + //~^ cast_lossless + let _: i16 = -1i8 as _; + //~^ cast_lossless + let _: u16 = (1u8 + 2) as _; + //~^ cast_lossless + let _: u32 = 1i8 as u16 as _; + //~^ cast_lossless } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, @@ -68,5 +122,30 @@ fn issue11458() { } let x = 10_u128; let _ = sign_cast!(x, u8, i8) as i32; + //~^ cast_lossless let _ = (sign_cast!(x, u8, i8) + 1) as i32; + //~^ cast_lossless +} + +fn issue12695() { + macro_rules! in_macro { + () => { + 1u8 as u32 + //~^ cast_lossless + }; + } + + let _ = in_macro!(); } + +fn ty_from_macro() { + macro_rules! ty { + () => { + u32 + }; + } + + let _ = 0u8 as ty!(); +} + +const IN_CONST: u64 = 0u8 as u64; diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr index aaece939285..c93ecb8fb56 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr @@ -1,137 +1,488 @@ -error: casting `i8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:9:13 +error: casts from `u8` to `u16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:8:5 | -LL | let _ = 1i8 as i16; - | ^^^^^^^^^^ help: try: `i16::from(1i8)` +LL | 0u8 as u16; + | ^^^^^^^^^^ | + = help: an `as` cast can become silently lossy if the types change in the future = note: `-D clippy::cast-lossless` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` +help: use `u16::from` instead + | +LL | u16::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `i16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:10:5 + | +LL | 0u8 as i16; + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i16::from` instead + | +LL | i16::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `u32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:12:5 + | +LL | 0u8 as u32; + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u32::from` instead + | +LL | u32::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:14:5 + | +LL | 0u8 as i32; + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead + | +LL | i32::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `u64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:16:5 + | +LL | 0u8 as u64; + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u64::from` instead + | +LL | u64::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:18:5 + | +LL | 0u8 as i64; + | ^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | i64::from(0u8); + | ~~~~~~~~~~~~~~ + +error: casts from `u8` to `u128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:20:5 + | +LL | 0u8 as u128; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u128::from` instead + | +LL | u128::from(0u8); + | ~~~~~~~~~~~~~~~ + +error: casts from `u8` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:22:5 + | +LL | 0u8 as i128; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0u8); + | ~~~~~~~~~~~~~~~ -error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:10:13 +error: casts from `u16` to `u32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:25:5 | -LL | let _ = 1i8 as i32; - | ^^^^^^^^^^ help: try: `i32::from(1i8)` +LL | 0u16 as u32; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u32::from` instead + | +LL | u32::from(0u16); + | ~~~~~~~~~~~~~~~ -error: casting `i8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:11:13 +error: casts from `u16` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:27:5 + | +LL | 0u16 as i32; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead | -LL | let _ = 1i8 as i64; - | ^^^^^^^^^^ help: try: `i64::from(1i8)` +LL | i32::from(0u16); + | ~~~~~~~~~~~~~~~ -error: casting `u8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:12:13 +error: casts from `u16` to `u64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:29:5 | -LL | let _ = 1u8 as i16; - | ^^^^^^^^^^ help: try: `i16::from(1u8)` +LL | 0u16 as u64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u64::from` instead + | +LL | u64::from(0u16); + | ~~~~~~~~~~~~~~~ -error: casting `u8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:13:13 +error: casts from `u16` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:31:5 + | +LL | 0u16 as i64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead | -LL | let _ = 1u8 as i32; - | ^^^^^^^^^^ help: try: `i32::from(1u8)` +LL | i64::from(0u16); + | ~~~~~~~~~~~~~~~ -error: casting `u8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:14:13 +error: casts from `u16` to `u128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:33:5 + | +LL | 0u16 as u128; + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u128::from` instead + | +LL | u128::from(0u16); + | ~~~~~~~~~~~~~~~~ + +error: casts from `u16` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:35:5 + | +LL | 0u16 as i128; + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0u16); + | ~~~~~~~~~~~~~~~~ + +error: casts from `u32` to `u64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:38:5 + | +LL | 0u32 as u64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u64::from` instead + | +LL | u64::from(0u32); + | ~~~~~~~~~~~~~~~ + +error: casts from `u32` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:40:5 + | +LL | 0u32 as i64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | i64::from(0u32); + | ~~~~~~~~~~~~~~~ + +error: casts from `u32` to `u128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:42:5 + | +LL | 0u32 as u128; + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u128::from` instead + | +LL | u128::from(0u32); + | ~~~~~~~~~~~~~~~~ + +error: casts from `u32` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:44:5 + | +LL | 0u32 as i128; + | ^^^^^^^^^^^^ | -LL | let _ = 1u8 as i64; - | ^^^^^^^^^^ help: try: `i64::from(1u8)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0u32); + | ~~~~~~~~~~~~~~~~ -error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:15:13 +error: casts from `u64` to `u128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:47:5 + | +LL | 0u64 as u128; + | ^^^^^^^^^^^^ | -LL | let _ = 1u8 as u16; - | ^^^^^^^^^^ help: try: `u16::from(1u8)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u128::from` instead + | +LL | u128::from(0u64); + | ~~~~~~~~~~~~~~~~ -error: casting `u8` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:16:13 +error: casts from `u64` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:49:5 + | +LL | 0u64 as i128; + | ^^^^^^^^^^^^ | -LL | let _ = 1u8 as u32; - | ^^^^^^^^^^ help: try: `u32::from(1u8)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0u64); + | ~~~~~~~~~~~~~~~~ -error: casting `u8` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:17:13 +error: casts from `i8` to `i16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:52:5 + | +LL | 0i8 as i16; + | ^^^^^^^^^^ | -LL | let _ = 1u8 as u64; - | ^^^^^^^^^^ help: try: `u64::from(1u8)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i16::from` instead + | +LL | i16::from(0i8); + | ~~~~~~~~~~~~~~ -error: casting `i16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:18:13 +error: casts from `i8` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:54:5 + | +LL | 0i8 as i32; + | ^^^^^^^^^^ | -LL | let _ = 1i16 as i32; - | ^^^^^^^^^^^ help: try: `i32::from(1i16)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead + | +LL | i32::from(0i8); + | ~~~~~~~~~~~~~~ -error: casting `i16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:19:13 +error: casts from `i8` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:56:5 + | +LL | 0i8 as i64; + | ^^^^^^^^^^ | -LL | let _ = 1i16 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1i16)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | i64::from(0i8); + | ~~~~~~~~~~~~~~ -error: casting `u16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:20:13 +error: casts from `i8` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:58:5 + | +LL | 0i8 as i128; + | ^^^^^^^^^^^ | -LL | let _ = 1u16 as i32; - | ^^^^^^^^^^^ help: try: `i32::from(1u16)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0i8); + | ~~~~~~~~~~~~~~~ -error: casting `u16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:21:13 +error: casts from `i16` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:61:5 + | +LL | 0i16 as i32; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead | -LL | let _ = 1u16 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1u16)` +LL | i32::from(0i16); + | ~~~~~~~~~~~~~~~ -error: casting `u16` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:22:13 +error: casts from `i16` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:63:5 | -LL | let _ = 1u16 as u32; - | ^^^^^^^^^^^ help: try: `u32::from(1u16)` +LL | 0i16 as i64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | i64::from(0i16); + | ~~~~~~~~~~~~~~~ -error: casting `u16` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:23:13 +error: casts from `i16` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:65:5 + | +LL | 0i16 as i128; + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead | -LL | let _ = 1u16 as u64; - | ^^^^^^^^^^^ help: try: `u64::from(1u16)` +LL | i128::from(0i16); + | ~~~~~~~~~~~~~~~~ -error: casting `i32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:24:13 +error: casts from `i32` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:68:5 | -LL | let _ = 1i32 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1i32)` +LL | 0i32 as i64; + | ^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i64::from` instead + | +LL | i64::from(0i32); + | ~~~~~~~~~~~~~~~ -error: casting `u32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:25:13 +error: casts from `i32` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:70:5 + | +LL | 0i32 as i128; + | ^^^^^^^^^^^^ | -LL | let _ = 1u32 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1u32)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0i32); + | ~~~~~~~~~~~~~~~~ -error: casting `u32` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:26:13 +error: casts from `i64` to `i128` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:73:5 + | +LL | 0i64 as i128; + | ^^^^^^^^^^^^ | -LL | let _ = 1u32 as u64; - | ^^^^^^^^^^^ help: try: `u64::from(1u32)` + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i128::from` instead + | +LL | i128::from(0i64); + | ~~~~~~~~~~~~~~~~ -error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:29:13 +error: casts from `u8` to `u16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:77:13 | LL | let _ = (1u8 + 1u8) as u16; - | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` + | ^^^^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `u16::from` instead + | +LL | let _ = u16::from(1u8 + 1u8); + | ~~~~~~~~~~~~~~~~~~~~ + +error: casts from `i8` to `i64` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:80:13 + | +LL | let _ = 1i8 as I64Alias; + | ^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `I64Alias::from` instead + | +LL | let _ = I64Alias::from(1i8); + | ~~~~~~~~~~~~~~~~~~~ + +error: casts from `u8` to `u16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:83:18 + | +LL | let _: u16 = 0u8 as _; + | ^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `Into::into` instead + | +LL | let _: u16 = 0u8.into(); + | ~~~~~~~~~~ -error: casting `i8` to `I64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:31:13 +error: casts from `i8` to `i16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:85:18 + | +LL | let _: i16 = -1i8 as _; + | ^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `Into::into` instead | -LL | let _ = 1i8 as I64; - | ^^^^^^^^^^ help: try: `I64::from(1i8)` +LL | let _: i16 = (-1i8).into(); + | ~~~~~~~~~~~~~ -error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:70:13 +error: casts from `u8` to `u16` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:87:18 + | +LL | let _: u16 = (1u8 + 2) as _; + | ^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `Into::into` instead + | +LL | let _: u16 = (1u8 + 2).into(); + | ~~~~~~~~~~~~~~~~ + +error: casts from `u16` to `u32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:89:18 + | +LL | let _: u32 = 1i8 as u16 as _; + | ^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `Into::into` instead + | +LL | let _: u32 = (1i8 as u16).into(); + | ~~~~~~~~~~~~~~~~~~~ + +error: casts from `i8` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:124:13 | LL | let _ = sign_cast!(x, u8, i8) as i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead + | +LL | let _ = i32::from(sign_cast!(x, u8, i8)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:71:13 +error: casts from `i8` to `i32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:126:13 | LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `i32::from` instead + | +LL | let _ = i32::from(sign_cast!(x, u8, i8) + 1); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: casts from `u8` to `u32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:133:13 + | +LL | 1u8 as u32 + | ^^^^^^^^^^ +... +LL | let _ = in_macro!(); + | ----------- in this macro invocation + | + = help: an `as` cast can become silently lossy if the types change in the future + = note: this error originates in the macro `in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `u32::from` instead + | +LL | u32::from(1u8) + | + +error: casts from `u8` to `u32` can be expressed infallibly using `From` + --> tests/ui/cast_lossless_integer.rs:148:13 + | +LL | let _ = 0u8 as ty!(); + | ^^^^^^^^^^^^ + | + = help: an `as` cast can become silently lossy if the types change in the future +help: use `<ty!()>::from` instead + | +LL | let _ = <ty!()>::from(0u8); + | ~~~~~~~~~~~~~~~~~~ -error: aborting due to 22 previous errors +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.fixed b/src/tools/clippy/tests/ui/crashes/ice-3717.fixed deleted file mode 100644 index 3f54b326979..00000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-3717.fixed +++ /dev/null @@ -1,11 +0,0 @@ -#![deny(clippy::implicit_hasher)] - -use std::collections::HashSet; - -fn main() {} - -pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { - //~^ ERROR: parameter of type `HashSet` should be generalized over different hashers - let _ = [0u8; 0]; - let _: HashSet<usize> = HashSet::default(); -} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.rs b/src/tools/clippy/tests/ui/crashes/ice-3717.rs index 2890a9277c7..770f6cf448a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3717.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.rs @@ -1,5 +1,7 @@ #![deny(clippy::implicit_hasher)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::collections::HashSet; fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr index 54b18a9641a..01627ff5b94 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr @@ -1,5 +1,5 @@ error: parameter of type `HashSet` should be generalized over different hashers - --> tests/ui/crashes/ice-3717.rs:7:21 + --> tests/ui/crashes/ice-3717.rs:9:21 | LL | pub fn ice_3717(_: &HashSet<usize>) { | ^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed index b6278c6ca8a..f412c57d650 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.fixed +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -45,7 +45,7 @@ fn main() { let _ = unsafe { *core::ptr::addr_of!(a) }; let _repeat = [0; 64]; - // do NOT lint for array as sematic differences with/out `*&`. + // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; } diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs index 572b0fdb102..67836b0de52 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.rs +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -45,7 +45,7 @@ fn main() { let _ = unsafe { *core::ptr::addr_of!(a) }; let _repeat = *&[0; 64]; - // do NOT lint for array as sematic differences with/out `*&`. + // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; } diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed deleted file mode 100644 index c85f384fd6e..00000000000 --- a/src/tools/clippy/tests/ui/derivable_impls.fixed +++ /dev/null @@ -1,295 +0,0 @@ -#![allow(dead_code)] - -use std::collections::HashMap; - -#[derive(Default)] -struct FooDefault<'a> { - a: bool, - b: i32, - c: u64, - d: Vec<i32>, - e: FooND1, - f: FooND2, - g: HashMap<i32, i32>, - h: (i32, Vec<i32>), - i: [Vec<i32>; 3], - j: [i32; 5], - k: Option<i32>, - l: &'a [i32], -} - - -#[derive(Default)] -struct TupleDefault(bool, i32, u64); - - -struct FooND1 { - a: bool, -} - -impl std::default::Default for FooND1 { - fn default() -> Self { - Self { a: true } - } -} - -struct FooND2 { - a: i32, -} - -impl std::default::Default for FooND2 { - fn default() -> Self { - Self { a: 5 } - } -} - -struct FooNDNew { - a: bool, -} - -impl FooNDNew { - fn new() -> Self { - Self { a: true } - } -} - -impl Default for FooNDNew { - fn default() -> Self { - Self::new() - } -} - -struct FooNDVec(Vec<i32>); - -impl Default for FooNDVec { - fn default() -> Self { - Self(vec![5, 12]) - } -} - -#[derive(Default)] -struct StrDefault<'a>(&'a str); - - -#[derive(Default)] -struct AlreadyDerived(i32, bool); - -macro_rules! mac { - () => { - 0 - }; - ($e:expr) => { - struct X(u32); - impl Default for X { - fn default() -> Self { - Self($e) - } - } - }; -} - -mac!(0); - -#[derive(Default)] -struct Y(u32); - -struct RustIssue26925<T> { - a: Option<T>, -} - -// We should watch out for cases where a manual impl is needed because a -// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925). -// For example, a struct with Option<T> does not require T: Default, but a derive adds -// that type bound anyways. So until #26925 get fixed we should disable lint -// for the following case -impl<T> Default for RustIssue26925<T> { - fn default() -> Self { - Self { a: None } - } -} - -struct SpecializedImpl<A, B> { - a: A, - b: B, -} - -impl<T: Default> Default for SpecializedImpl<T, T> { - fn default() -> Self { - Self { - a: T::default(), - b: T::default(), - } - } -} - -#[derive(Default)] -struct WithoutSelfCurly { - a: bool, -} - - -#[derive(Default)] -struct WithoutSelfParan(bool); - - -// https://github.com/rust-lang/rust-clippy/issues/7655 - -pub struct SpecializedImpl2<T> { - v: Vec<T>, -} - -impl Default for SpecializedImpl2<String> { - fn default() -> Self { - Self { v: Vec::new() } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7654 - -pub struct Color { - pub r: u8, - pub g: u8, - pub b: u8, -} - -/// `#000000` -impl Default for Color { - fn default() -> Self { - Color { r: 0, g: 0, b: 0 } - } -} - -pub struct Color2 { - pub r: u8, - pub g: u8, - pub b: u8, -} - -impl Default for Color2 { - /// `#000000` - fn default() -> Self { - Self { r: 0, g: 0, b: 0 } - } -} - -#[derive(Default)] -pub struct RepeatDefault1 { - a: [i8; 32], -} - - -pub struct RepeatDefault2 { - a: [i8; 33], -} - -impl Default for RepeatDefault2 { - fn default() -> Self { - RepeatDefault2 { a: [0; 33] } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7753 - -pub enum IntOrString { - Int(i32), - String(String), -} - -impl Default for IntOrString { - fn default() -> Self { - IntOrString::Int(0) - } -} - -#[derive(Default)] -pub enum SimpleEnum { - Foo, - #[default] - Bar, -} - - -pub enum NonExhaustiveEnum { - Foo, - #[non_exhaustive] - Bar, -} - -impl Default for NonExhaustiveEnum { - fn default() -> Self { - NonExhaustiveEnum::Bar - } -} - -// https://github.com/rust-lang/rust-clippy/issues/10396 - -#[derive(Default)] -struct DefaultType; - -struct GenericType<T = DefaultType> { - t: T, -} - -impl Default for GenericType { - fn default() -> Self { - Self { t: Default::default() } - } -} - -struct InnerGenericType<T> { - t: T, -} - -impl Default for InnerGenericType<DefaultType> { - fn default() -> Self { - Self { t: Default::default() } - } -} - -struct OtherGenericType<T = DefaultType> { - inner: InnerGenericType<T>, -} - -impl Default for OtherGenericType { - fn default() -> Self { - Self { - inner: Default::default(), - } - } -} - -mod issue10158 { - pub trait T {} - - #[derive(Default)] - pub struct S {} - impl T for S {} - - pub struct Outer { - pub inner: Box<dyn T>, - } - - impl Default for Outer { - fn default() -> Self { - Outer { - // Box::<S>::default() adjusts to Box<dyn T> - inner: Box::<S>::default(), - } - } - } -} - -mod issue11368 { - pub struct A { - a: u32, - } - - impl Default for A { - #[track_caller] - fn default() -> Self { - Self { a: 0 } - } - } -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs index 21d73ba8b77..58f7771b627 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.rs +++ b/src/tools/clippy/tests/ui/derivable_impls.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::collections::HashMap; struct FooDefault<'a> { diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr index 0adb422373d..d3adfa60e10 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.stderr +++ b/src/tools/clippy/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:20:1 + --> tests/ui/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | fn default() -> Self { @@ -20,7 +20,7 @@ LL | struct FooDefault<'a> { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:41:1 + --> tests/ui/derivable_impls.rs:43:1 | LL | / impl std::default::Default for TupleDefault { LL | | fn default() -> Self { @@ -37,7 +37,7 @@ LL | struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:93:1 + --> tests/ui/derivable_impls.rs:95:1 | LL | / impl Default for StrDefault<'_> { LL | | fn default() -> Self { @@ -54,7 +54,7 @@ LL | struct StrDefault<'a>(&'a str); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:119:1 + --> tests/ui/derivable_impls.rs:121:1 | LL | / impl Default for Y { LL | | fn default() -> Self { @@ -71,7 +71,7 @@ LL | struct Y(u32); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:158:1 + --> tests/ui/derivable_impls.rs:160:1 | LL | / impl Default for WithoutSelfCurly { LL | | fn default() -> Self { @@ -88,7 +88,7 @@ LL | struct WithoutSelfCurly { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:166:1 + --> tests/ui/derivable_impls.rs:168:1 | LL | / impl Default for WithoutSelfParan { LL | | fn default() -> Self { @@ -105,7 +105,7 @@ LL | struct WithoutSelfParan(bool); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:216:1 + --> tests/ui/derivable_impls.rs:218:1 | LL | / impl Default for RepeatDefault1 { LL | | fn default() -> Self { @@ -122,7 +122,7 @@ LL | pub struct RepeatDefault1 { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:250:1 + --> tests/ui/derivable_impls.rs:252:1 | LL | / impl Default for SimpleEnum { LL | | fn default() -> Self { diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 84673f1f43f..edfffe8fcfe 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -54,21 +54,24 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB -/// DirectX +/// AccessKit +/// CoreFoundation CoreGraphics CoreText +/// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry -/// WebGL WebGL2 WebGPU +/// OpenAL OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry +/// OpenType +/// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD +/// iOS macOS FreeBSD NetBSD OpenBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 4d017a99e0f..3c0f6913e32 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -54,21 +54,24 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB -/// DirectX +/// AccessKit +/// CoreFoundation CoreGraphics CoreText +/// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry -/// WebGL WebGL2 WebGPU +/// OpenAL OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry +/// OpenType +/// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD +/// iOS macOS FreeBSD NetBSD OpenBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index a9263f62d38..67c0464149c 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:75:5 + --> tests/ui/doc/doc-fixable.rs:78:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:92:5 + --> tests/ui/doc/doc-fixable.rs:95:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:100:8 + --> tests/ui/doc/doc-fixable.rs:103:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:103:7 + --> tests/ui/doc/doc-fixable.rs:106:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:105:22 + --> tests/ui/doc/doc-fixable.rs:108:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:106:5 + --> tests/ui/doc/doc-fixable.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:113:5 + --> tests/ui/doc/doc-fixable.rs:116:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:126:5 + --> tests/ui/doc/doc-fixable.rs:129:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:137:43 + --> tests/ui/doc/doc-fixable.rs:140:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:142:5 + --> tests/ui/doc/doc-fixable.rs:145:5 | LL | And BarQuz too. | ^^^^^^ @@ -243,7 +243,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:143:1 + --> tests/ui/doc/doc-fixable.rs:146:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:148:43 + --> tests/ui/doc/doc-fixable.rs:151:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:153:5 + --> tests/ui/doc/doc-fixable.rs:156:5 | LL | And BarQuz too. | ^^^^^^ @@ -276,7 +276,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:154:1 + --> tests/ui/doc/doc-fixable.rs:157:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:165:5 + --> tests/ui/doc/doc-fixable.rs:168:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:184:22 + --> tests/ui/doc/doc-fixable.rs:187:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:208:34 + --> tests/ui/doc/doc-fixable.rs:211:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | ~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:231:22 + --> tests/ui/doc/doc-fixable.rs:234:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()). | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:231:30 + --> tests/ui/doc/doc-fixable.rs:234:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`). | ~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:5 + --> tests/ui/doc/doc-fixable.rs:237:5 | LL | /// ABes | ^^^^ @@ -353,7 +353,7 @@ LL | /// `ABes` | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:9 + --> tests/ui/doc/doc-fixable.rs:243:9 | LL | /// foo() | ^^^^^ @@ -364,7 +364,7 @@ LL | /// `foo()` | ~~~~~~~ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:244:5 + --> tests/ui/doc/doc-fixable.rs:247:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>` diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed index cc553103530..372f64f7d99 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.fixed +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -78,4 +78,8 @@ fn main() { const NEG_INF1: f32 = -1.0e+33f32; const NEG_INF2: f64 = -1.0e+3300f64; const NEG_INF3: f32 = -3.40282357e+38_f32; + + // issue #12954 + const _: f64 = 3.0; + const _: f64 = 3.0000000000000000; } diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs index fff986a8296..1e40efbf245 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.rs +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -78,4 +78,8 @@ fn main() { const NEG_INF1: f32 = -1.0e+33f32; const NEG_INF2: f64 = -1.0e+3300f64; const NEG_INF3: f32 = -3.40282357e+38_f32; + + // issue #12954 + const _: f64 = 3.0000000000000000e+00; + const _: f64 = 3.0000000000000000; } diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index 22dd96e53bd..6d8e166a649 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -91,5 +91,11 @@ error: float has excessive precision LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64` -error: aborting due to 15 previous errors +error: float has excessive precision + --> tests/ui/excessive_precision.rs:83:20 + | +LL | const _: f64 = 3.0000000000000000e+00; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `3.0` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed index a44938fdf69..25f09a63795 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.fixed +++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed @@ -25,6 +25,14 @@ fn main() { let _ = 1.5_f64.sqrt(); let _ = 1.5_f64.powi(3); + macro_rules! m { + ($e:expr) => { + 5.5 - $e + }; + } + + let _ = (1f32 + m!(2.0)).exp2(); + // Cases where the lint shouldn't be applied let _ = x.powf(2.1); let _ = x.powf(-2.1); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs index 80f6c1791d7..9e9878de4ba 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.rs +++ b/src/tools/clippy/tests/ui/floating_point_powf.rs @@ -25,6 +25,14 @@ fn main() { let _ = 1.5_f64.powf(1.0 / 2.0); let _ = 1.5_f64.powf(3.0); + macro_rules! m { + ($e:expr) => { + 5.5 - $e + }; + } + + let _ = 2f32.powf(1f32 + m!(2.0)); + // Cases where the lint shouldn't be applied let _ = x.powf(2.1); let _ = x.powf(-2.1); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr index 671383401b5..c944f14fa34 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.stderr +++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr @@ -119,76 +119,82 @@ LL | let _ = 1.5_f64.powf(3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:35:13 + --> tests/ui/floating_point_powf.rs:34:13 + | +LL | let _ = 2f32.powf(1f32 + m!(2.0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1f32 + m!(2.0)).exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> tests/ui/floating_point_powf.rs:43:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:36:13 + --> tests/ui/floating_point_powf.rs:44:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:37:13 + --> tests/ui/floating_point_powf.rs:45:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:38:13 + --> tests/ui/floating_point_powf.rs:46:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:39:13 + --> tests/ui/floating_point_powf.rs:47:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:40:13 + --> tests/ui/floating_point_powf.rs:48:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> tests/ui/floating_point_powf.rs:41:13 + --> tests/ui/floating_point_powf.rs:49:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:42:13 + --> tests/ui/floating_point_powf.rs:50:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:43:13 + --> tests/ui/floating_point_powf.rs:51:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:44:13 + --> tests/ui/floating_point_powf.rs:52:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:45:13 + --> tests/ui/floating_point_powf.rs:53:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:46:13 + --> tests/ui/floating_point_powf.rs:54:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` -error: aborting due to 31 previous errors +error: aborting due to 32 previous errors diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed index f9ce1defda1..6c582190b44 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed +++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs index 2d5b351f8da..0df6a0a202a 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.rs +++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 01a1bf8940a..4aa84eca261 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:29:5 + --> tests/ui/from_str_radix_10.rs:28:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:32:5 + --> tests/ui/from_str_radix_10.rs:31:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:34:5 + --> tests/ui/from_str_radix_10.rs:33:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:36:5 + --> tests/ui/from_str_radix_10.rs:35:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:38:5 + --> tests/ui/from_str_radix_10.rs:37:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:40:5 + --> tests/ui/from_str_radix_10.rs:39:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:44:5 + --> tests/ui/from_str_radix_10.rs:43:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:48:5 + --> tests/ui/from_str_radix_10.rs:47:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()` diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed deleted file mode 100644 index 13f0cbe9cc8..00000000000 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ /dev/null @@ -1,177 +0,0 @@ -#![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args)] - -enum SomeEnum<T> { - One(T), - Two(T), - Three(T), - Four(T), -} - -fn lintable_examples() { - // Try with reference - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Try with copy - let slice: Option<[u32; 3]> = Some([1, 2, 3]); - if let Some([slice_0, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Try with long slice and small indices - let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]); - if let Some([slice_0, _, slice_2, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_2); - println!("{}", slice_0); - } - - // Multiple bindings - let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]); - if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{}", slice_0); - } - - // Two lintable slices in one if let - let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]); - let b_wrapped: Option<[u32; 2]> = Some([4, 6]); - if let (SomeEnum::Three([_, _, a_2, ..]), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - //~| ERROR: this binding can be a slice pattern to avoid indexing - println!("{} -> {}", a_2, b_1); - } - - // This requires the slice values to be borrowed as the slice values can only be - // borrowed and `String` doesn't implement copy - let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); - if let Some([_, ref slice_1, ..]) = slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{:?}", slice_1); - } - println!("{:?}", slice); - - // This should not suggest using the `ref` keyword as the scrutinee is already - // a reference - let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); - if let Some([slice_0, ..]) = &slice { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - println!("{:?}", slice_0); - } - println!("{:?}", slice); -} - -fn slice_index_above_limit() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - - if let Some(slice) = slice { - // Would cause a panic, IDK - println!("{}", slice[7]); - } -} - -fn slice_is_used() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{:?}", slice.len()); - } - - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{:?}", slice.to_vec()); - } - - let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); - if let Some(slice) = opt { - if !slice.is_empty() { - println!("first: {}", slice[0]); - } - } -} - -/// The slice is used by an external function and should therefore not be linted -fn check_slice_as_arg() { - fn is_interesting<T>(slice: &[T; 2]) -> bool { - !slice.is_empty() - } - - let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); - if let Some(slice) = &slice_wrapped { - if is_interesting(slice) { - println!("This is interesting {}", slice[0]); - } - } - println!("{:?}", slice_wrapped); -} - -fn check_slice_in_struct() { - #[derive(Debug)] - struct Wrapper<'a> { - inner: Option<&'a [String]>, - is_awesome: bool, - } - - impl<'a> Wrapper<'a> { - fn is_super_awesome(&self) -> bool { - self.is_awesome - } - } - - let inner = &[String::from("New"), String::from("World")]; - let wrap = Wrapper { - inner: Some(inner), - is_awesome: true, - }; - - // Test 1: Field access - if let Some([slice_0, ..]) = wrap.inner { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - if wrap.is_awesome { - println!("This is awesome! {}", slice_0); - } - } - - // Test 2: function access - if let Some([slice_0, ..]) = wrap.inner { - //~^ ERROR: this binding can be a slice pattern to avoid indexing - if wrap.is_super_awesome() { - println!("This is super awesome! {}", slice_0); - } - } - println!("Complete wrap: {:?}", wrap); -} - -/// This would be a nice additional feature to have in the future, but adding it -/// now would make the PR too large. This is therefore only a test that we don't -/// lint cases we can't make a reasonable suggestion for -fn mutable_slice_index() { - // Mut access - let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]); - if let Some(ref mut slice) = slice { - slice[0] = String::from("Mr. Penguin"); - } - println!("Use after modification: {:?}", slice); - - // Mut access on reference - let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); - if let Some(slice) = &mut slice { - slice[0] = String::from("Lord Meow Meow"); - } - println!("Use after modification: {:?}", slice); -} - -/// The lint will ignore bindings with sub patterns as it would be hard -/// to build correct suggestions for these instances :) -fn binding_with_sub_pattern() { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice @ [_, _, _]) = slice { - println!("{:?}", slice[2]); - } -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs index d8d38c167fa..5bbdabcaad1 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,6 +1,8 @@ #![deny(clippy::index_refutable_slice)] #![allow(clippy::uninlined_format_args)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + enum SomeEnum<T> { One(T), Two(T), diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index 14e0f931f24..8819cb0e28b 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:14:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:16:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:21:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:23:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -34,7 +34,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:28:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:30:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -50,7 +50,7 @@ LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:36:26 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:38:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ @@ -65,7 +65,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:29 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -80,7 +80,7 @@ LL | println!("{} -> {}", a_2, b[1]); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:38 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -95,7 +95,7 @@ LL | println!("{} -> {}", a[2], b_1); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:53:21 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:55:21 | LL | if let Some(ref slice) = slice { | ^^^^^ @@ -110,7 +110,7 @@ LL | println!("{:?}", slice_1); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:62:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:64:17 | LL | if let Some(slice) = &slice { | ^^^^^ @@ -125,7 +125,7 @@ LL | println!("{:?}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:132:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:134:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ @@ -140,7 +140,7 @@ LL | println!("This is awesome! {}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:140:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:142:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed deleted file mode 100644 index 72edc539f04..00000000000 --- a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![deny(clippy::index_refutable_slice)] - -extern crate if_chain; -use if_chain::if_chain; - -macro_rules! if_let_slice_macro { - () => { - // This would normally be linted - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice { - println!("{}", slice[0]); - } - }; -} - -fn main() { - // Don't lint this - if_let_slice_macro!(); - - // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice; - //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice_0); - } - } -} diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs index 7b474ba423b..5d9fad48889 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs @@ -1,5 +1,7 @@ #![deny(clippy::index_refutable_slice)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + extern crate if_chain; use if_chain::if_chain; diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr index caccd0f9295..69f0aaa9777 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr +++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:23:21 + --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:25:21 | LL | if let Some(slice) = slice; | ^^^^^ diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed deleted file mode 100644 index 3456e274f6a..00000000000 --- a/src/tools/clippy/tests/ui/let_unit.fixed +++ /dev/null @@ -1,196 +0,0 @@ -#![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] - -macro_rules! let_and_return { - ($n:expr) => {{ - let ret = $n; - }}; -} - -fn main() { - println!("x"); - let _y = 1; // this is fine - let _z = ((), 1); // this as well - if true { - // do not lint this, since () is explicit - let _a = (); - let () = dummy(); - let () = (); - () = dummy(); - () = (); - let _a: () = (); - let _a: () = dummy(); - } - - consume_units_with_for_loop(); // should be fine as well - - multiline_sugg(); - - let_and_return!(()) // should be fine -} - -fn dummy() {} - -// Related to issue #1964 -fn consume_units_with_for_loop() { - // `for_let_unit` lint should not be triggered by consuming them using for loop. - let v = vec![(), (), ()]; - let mut count = 0; - for _ in v { - count += 1; - } - assert_eq!(count, 3); - - // Same for consuming from some other Iterator<Item = ()>. - let (tx, rx) = ::std::sync::mpsc::channel(); - tx.send(()).unwrap(); - drop(tx); - - count = 0; - for _ in rx.iter() { - count += 1; - } - assert_eq!(count, 1); -} - -fn multiline_sugg() { - let v: Vec<u8> = vec![2]; - - v - .into_iter() - .map(|i| i * 2) - .filter(|i| i % 2 == 0) - .map(|_| ()) - .next() - .unwrap(); -} - -#[derive(Copy, Clone)] -pub struct ContainsUnit(()); // should be fine - -fn _returns_generic() { - fn f<T>() -> T { - unimplemented!() - } - fn f2<T, U>(_: T) -> U { - unimplemented!() - } - fn f3<T>(x: T) -> T { - x - } - fn f5<T: Default>(x: bool) -> Option<T> { - x.then(|| T::default()) - } - - let _: () = f(); - let x: () = f(); - - let _: () = f2(0i32); - let x: () = f2(0i32); - - let _: () = f3(()); - let x: () = f3(()); - - fn f4<T>(mut x: Vec<T>) -> T { - x.pop().unwrap() - } - let _: () = f4(vec![()]); - let x: () = f4(vec![()]); - - let _: () = { - let x = 5; - f2(x) - }; - - let _: () = if true { f() } else { f2(0) }; - let x: () = if true { f() } else { f2(0) }; - - match Some(0) { - None => f2(1), - Some(0) => f(), - Some(1) => f2(3), - Some(_) => (), - }; - - let _: () = f5(true).unwrap(); - - #[allow(clippy::let_unit_value)] - { - let x = f(); - let y; - let z; - match 0 { - 0 => { - y = f(); - z = f(); - }, - 1 => { - println!("test"); - y = f(); - z = f3(()); - }, - _ => panic!(), - } - - let x1; - let x2; - if true { - x1 = f(); - x2 = x1; - } else { - x2 = f(); - x1 = x2; - } - - let opt; - match f5(true) { - Some(x) => opt = x, - None => panic!(), - }; - - #[warn(clippy::let_unit_value)] - { - let _: () = x; - let _: () = y; - let _: () = z; - let _: () = x1; - let _: () = x2; - let _: () = opt; - } - } - - let () = f(); -} - -fn attributes() { - fn f() {} - - #[allow(clippy::let_unit_value)] - let _ = f(); - #[expect(clippy::let_unit_value)] - let _ = f(); -} - -async fn issue10433() { - let _pending: () = std::future::pending().await; -} - -pub async fn issue11502(a: ()) {} - -pub fn issue12594() { - fn returns_unit() {} - - fn returns_result<T>(res: T) -> Result<T, ()> { - Ok(res) - } - - fn actual_test() { - // create first a unit value'd value - returns_unit(); - returns_result(()).unwrap(); - returns_result(()).unwrap(); - // make sure we replace only the first variable - let res = 1; - returns_result(res).unwrap(); - } -} diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index e2dafbcb771..530103ffaf6 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -1,6 +1,8 @@ #![warn(clippy::let_unit_value)] #![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + macro_rules! let_and_return { ($n:expr) => {{ let ret = $n; diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr index 2f62c33c887..6f149454af2 100644 --- a/src/tools/clippy/tests/ui/let_unit.stderr +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -1,5 +1,5 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:13:5 | LL | let _x = println!("x"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` @@ -8,7 +8,7 @@ LL | let _x = println!("x"); = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` error: this let-binding has unit value - --> tests/ui/let_unit.rs:59:5 + --> tests/ui/let_unit.rs:61:5 | LL | / let _ = v LL | | .into_iter() @@ -31,7 +31,7 @@ LL + .unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:108:5 + --> tests/ui/let_unit.rs:110:5 | LL | / let x = match Some(0) { LL | | None => f2(1), @@ -52,7 +52,7 @@ LL + }; | error: this let-binding has unit value - --> tests/ui/let_unit.rs:189:9 + --> tests/ui/let_unit.rs:191:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed deleted file mode 100644 index 75835780801..00000000000 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed +++ /dev/null @@ -1,76 +0,0 @@ -//@revisions: edition2018 edition2021 -//@[edition2018] edition:2018 -//@[edition2021] edition:2021 - -#![warn(clippy::manual_assert)] -#![allow(dead_code, unused_doc_comments)] -#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] - -macro_rules! one { - () => { - 1 - }; -} - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - assert!(!a.is_empty(), "with expansion {}", one!()); - if a.is_empty() { - let _ = 0; - } else if a.len() == 1 { - panic!("panic6"); - } -} - -fn issue7730(a: u8) { - // Suggestion should preserve comment - // comment -/* this is a - multiline - comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); -} - -fn issue12505() { - struct Foo<T, const N: usize>(T); - - impl<T, const N: usize> Foo<T, N> { - const BAR: () = assert!(!(N == 0), ); - } -} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr index 1eebe1bfe17..004463720e2 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:30:5 + --> tests/ui/manual_assert.rs:32:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); @@ -10,7 +10,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:33:5 + --> tests/ui/manual_assert.rs:35:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); @@ -18,7 +18,7 @@ LL | | } | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:50:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); @@ -26,7 +26,7 @@ LL | | } | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:53:5 + --> tests/ui/manual_assert.rs:55:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); @@ -34,7 +34,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:56:5 + --> tests/ui/manual_assert.rs:58:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); @@ -42,7 +42,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:59:5 + --> tests/ui/manual_assert.rs:61:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); @@ -50,7 +50,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); @@ -58,7 +58,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:65:5 + --> tests/ui/manual_assert.rs:67:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) @@ -66,7 +66,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:77:5 + --> tests/ui/manual_assert.rs:79:5 | LL | / if a > 2 { LL | | // comment @@ -83,7 +83,7 @@ LL | assert!(!(a > 2), "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:91:25 + --> tests/ui/manual_assert.rs:93:25 | LL | const BAR: () = if N == 0 { | _________________________^ diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed deleted file mode 100644 index 75835780801..00000000000 --- a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed +++ /dev/null @@ -1,76 +0,0 @@ -//@revisions: edition2018 edition2021 -//@[edition2018] edition:2018 -//@[edition2021] edition:2021 - -#![warn(clippy::manual_assert)] -#![allow(dead_code, unused_doc_comments)] -#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] - -macro_rules! one { - () => { - 1 - }; -} - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - assert!(!a.is_empty(), "with expansion {}", one!()); - if a.is_empty() { - let _ = 0; - } else if a.len() == 1 { - panic!("panic6"); - } -} - -fn issue7730(a: u8) { - // Suggestion should preserve comment - // comment -/* this is a - multiline - comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); -} - -fn issue12505() { - struct Foo<T, const N: usize>(T); - - impl<T, const N: usize> Foo<T, N> { - const BAR: () = assert!(!(N == 0), ); - } -} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr index 1eebe1bfe17..004463720e2 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:30:5 + --> tests/ui/manual_assert.rs:32:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); @@ -10,7 +10,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:33:5 + --> tests/ui/manual_assert.rs:35:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); @@ -18,7 +18,7 @@ LL | | } | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:50:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); @@ -26,7 +26,7 @@ LL | | } | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:53:5 + --> tests/ui/manual_assert.rs:55:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); @@ -34,7 +34,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:56:5 + --> tests/ui/manual_assert.rs:58:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); @@ -42,7 +42,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:59:5 + --> tests/ui/manual_assert.rs:61:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); @@ -50,7 +50,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); @@ -58,7 +58,7 @@ LL | | } | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:65:5 + --> tests/ui/manual_assert.rs:67:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) @@ -66,7 +66,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:77:5 + --> tests/ui/manual_assert.rs:79:5 | LL | / if a > 2 { LL | | // comment @@ -83,7 +83,7 @@ LL | assert!(!(a > 2), "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:91:25 + --> tests/ui/manual_assert.rs:93:25 | LL | const BAR: () = if N == 0 { | _________________________^ diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs index 363bafdf05d..6337920a3ee 100644 --- a/src/tools/clippy/tests/ui/manual_assert.rs +++ b/src/tools/clippy/tests/ui/manual_assert.rs @@ -2,6 +2,8 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 +//@no-rustfix: need to change the suggestion to a multipart suggestion + #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed deleted file mode 100644 index 18444090a42..00000000000 --- a/src/tools/clippy/tests/ui/manual_async_fn.fixed +++ /dev/null @@ -1,115 +0,0 @@ -#![warn(clippy::manual_async_fn)] -#![allow(clippy::needless_pub_self, unused)] - -use std::future::Future; - -async fn fut() -> i32 { 42 } - -#[rustfmt::skip] -async fn fut2() -> i32 { 42 } - -#[rustfmt::skip] -async fn fut3() -> i32 { 42 } - -async fn empty_fut() {} - -#[rustfmt::skip] -async fn empty_fut2() {} - -#[rustfmt::skip] -async fn empty_fut3() {} - -async fn core_fut() -> i32 { 42 } - -// should be ignored -fn has_other_stmts() -> impl core::future::Future<Output = i32> { - let _ = 42; - async move { 42 } -} - -// should be ignored -fn not_fut() -> i32 { - 42 -} - -// should be ignored -async fn already_async() -> impl Future<Output = i32> { - async { 42 } -} - -struct S; -impl S { - async fn inh_fut() -> i32 { - // NOTE: this code is here just to check that the indentation is correct in the suggested fix - let a = 42; - let b = 21; - if a < b { - let c = 21; - let d = 42; - if c < d { - let _ = 42; - } - } - 42 - } - - // should be ignored - fn not_fut(&self) -> i32 { - 42 - } - - // should be ignored - fn has_other_stmts() -> impl core::future::Future<Output = i32> { - let _ = 42; - async move { 42 } - } - - // should be ignored - async fn already_async(&self) -> impl Future<Output = i32> { - async { 42 } - } -} - -// Tests related to lifetime capture - -async fn elided(_: &i32) -> i32 { 42 } - -// should be ignored -fn elided_not_bound(_: &i32) -> impl Future<Output = i32> { - async { 42 } -} - -async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } - -// should be ignored -#[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> { - async { 42 } -} - -// should be ignored -mod issue_5765 { - use std::future::Future; - - struct A; - impl A { - fn f(&self) -> impl Future<Output = ()> { - async {} - } - } - - fn test() { - let _future = { - let a = A; - a.f() - }; - } -} - -pub async fn issue_10450() -> i32 { 42 } - -pub(crate) async fn issue_10450_2() -> i32 { 42 } - -pub(self) async fn issue_10450_3() -> i32 { 42 } - -fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index d42165bbe3d..6b8ac5033a9 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -1,6 +1,8 @@ #![warn(clippy::manual_async_fn)] #![allow(clippy::needless_pub_self, unused)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::future::Future; fn fut() -> impl Future<Output = i32> { diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr index c50af5a4988..f88fc30b3b5 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.stderr +++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr @@ -1,5 +1,5 @@ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:6:1 + --> tests/ui/manual_async_fn.rs:8:1 | LL | fn fut() -> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn fut() -> impl Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:11:1 + --> tests/ui/manual_async_fn.rs:13:1 | LL | fn fut2() ->impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | fn fut2() ->impl Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:16:1 + --> tests/ui/manual_async_fn.rs:18:1 | LL | fn fut3()-> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | fn fut3()-> impl Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:20:1 + --> tests/ui/manual_async_fn.rs:22:1 | LL | fn empty_fut() -> impl Future<Output = ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | fn empty_fut() -> impl Future<Output = ()> {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:25:1 + --> tests/ui/manual_async_fn.rs:27:1 | LL | fn empty_fut2() ->impl Future<Output = ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | fn empty_fut2() ->impl Future<Output = ()> {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:30:1 + --> tests/ui/manual_async_fn.rs:32:1 | LL | fn empty_fut3()-> impl Future<Output = ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | fn empty_fut3()-> impl Future<Output = ()> {} | ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:34:1 + --> tests/ui/manual_async_fn.rs:36:1 | LL | fn core_fut() -> impl core::future::Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:56:5 + --> tests/ui/manual_async_fn.rs:58:5 | LL | fn inh_fut() -> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + } | error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:91:1 + --> tests/ui/manual_async_fn.rs:93:1 | LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,7 +148,7 @@ LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:100:1 + --> tests/ui/manual_async_fn.rs:102:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:129:1 + --> tests/ui/manual_async_fn.rs:131:1 | LL | pub fn issue_10450() -> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +178,7 @@ LL | pub fn issue_10450() -> impl Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:133:1 + --> tests/ui/manual_async_fn.rs:135:1 | LL | pub(crate) fn issue_10450_2() -> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL | pub(crate) fn issue_10450_2() -> impl Future<Output = i32> { 42 } | ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:137:1 + --> tests/ui/manual_async_fn.rs:139:1 | LL | pub(self) fn issue_10450_3() -> impl Future<Output = i32> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed deleted file mode 100644 index aaac6a048e1..00000000000 --- a/src/tools/clippy/tests/ui/manual_split_once.fixed +++ /dev/null @@ -1,144 +0,0 @@ -#![warn(clippy::manual_split_once)] -#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] - -extern crate itertools; - -#[allow(unused_imports)] -use itertools::Itertools; - -fn main() { - let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').unwrap().1; - let _ = "key=value".split_once('=').unwrap().1; - let (_, _) = "key=value".split_once('=').unwrap(); - - let s = String::from("key=value"); - let _ = s.split_once('=').unwrap().1; - - let s = Box::<str>::from("key=value"); - let _ = s.split_once('=').unwrap().1; - - let s = &"key=value"; - let _ = s.split_once('=').unwrap().1; - - fn _f(s: &str) -> Option<&str> { - let _ = s.split_once('=')?.1; - let _ = s.split_once('=')?.1; - let _ = s.rsplit_once('=')?.0; - let _ = s.rsplit_once('=')?.0; - None - } - - // Don't lint, slices don't have `split_once` - let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); - - // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplit_once('=').unwrap().0; - let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); - let _ = s.rsplit_once('=').map(|x| x.0); -} - -fn indirect() -> Option<()> { - let (l, r) = "a.b.c".split_once('.').unwrap(); - - - - let (l, r) = "a.b.c".split_once('.')?; - - - - let (l, r) = "a.b.c".rsplit_once('.').unwrap(); - - - - let (l, r) = "a.b.c".rsplit_once('.')?; - - - - // could lint, currently doesn't - - let mut iter = "a.b.c".splitn(2, '.'); - let other = 1; - let l = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let mut mut_binding = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let tuple = (iter.next()?, iter.next()?); - - // should not lint - - let mut missing_unwrap = "a.b.c".splitn(2, '.'); - let l = missing_unwrap.next(); - let r = missing_unwrap.next(); - - let mut mixed_unrap = "a.b.c".splitn(2, '.'); - let unwrap = mixed_unrap.next().unwrap(); - let question_mark = mixed_unrap.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let same_name = iter.next()?; - let same_name = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let shadows_existing = "d"; - let shadows_existing = iter.next()?; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let becomes_shadowed = iter.next()?; - let becomes_shadowed = "d"; - let r = iter.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - let l = iter.next()?; - let r = iter.next()?; - let third_usage = iter.next()?; - - let mut n_three = "a.b.c".splitn(3, '.'); - let l = n_three.next()?; - let r = n_three.next()?; - - let mut iter = "a.b.c".splitn(2, '.'); - { - let in_block = iter.next()?; - } - let r = iter.next()?; - - let mut lacks_binding = "a.b.c".splitn(2, '.'); - let _ = lacks_binding.next()?; - let r = lacks_binding.next()?; - - let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); - let l = iter.next()?; - let r = iter.next()?; - - let mut assigned = ""; - let mut iter = "a.b.c".splitn(2, '.'); - let l = iter.next()?; - assigned = iter.next()?; - - None -} - -#[clippy::msrv = "1.51"] -fn _msrv_1_51() { - // `str::split_once` was stabilized in 1.52. Do not lint this - let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - - let mut iter = "a.b.c".splitn(2, '.'); - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); -} - -#[clippy::msrv = "1.52"] -fn _msrv_1_52() { - let _ = "key=value".split_once('=').unwrap().1; - - let (a, b) = "a.b.c".split_once('.').unwrap(); - - -} diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs index 113e1737c97..e13c827468b 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.rs +++ b/src/tools/clippy/tests/ui/manual_split_once.rs @@ -1,6 +1,8 @@ #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + extern crate itertools; #[allow(unused_imports)] diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr index c5c9be3ac63..566204ad876 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.stderr +++ b/src/tools/clippy/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:11:13 + --> tests/ui/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` @@ -8,79 +8,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = help: to override `-D warnings` add `#[allow(clippy::manual_split_once)]` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:12:13 + --> tests/ui/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:13:18 + --> tests/ui/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:16:13 + --> tests/ui/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:19:13 + --> tests/ui/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:22:13 + --> tests/ui/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:25:17 + --> tests/ui/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:26:17 + --> tests/ui/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:27:17 + --> tests/ui/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:28:17 + --> tests/ui/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:36:13 + --> tests/ui/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:37:18 + --> tests/ui/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:38:13 + --> tests/ui/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:42:5 + --> tests/ui/manual_split_once.rs:44:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL - let r = iter.next().unwrap(); | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:46:5 + --> tests/ui/manual_split_once.rs:48:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL - let r = iter.next()?; | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:50:5 + --> tests/ui/manual_split_once.rs:52:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL - let l = iter.next().unwrap(); | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:54:5 + --> tests/ui/manual_split_once.rs:56:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -172,13 +172,13 @@ LL - let l = iter.next()?; | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:139:13 + --> tests/ui/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:141:5 + --> tests/ui/manual_split_once.rs:143:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 74afa00e12f..07e4bdd483a 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -234,4 +234,20 @@ fn implicit_deref_ref() { }; } +mod issue_13018 { + use std::collections::HashMap; + + type RefName = i32; + pub fn get(index: &HashMap<usize, Vec<RefName>>, id: usize) -> &[RefName] { + if let Some(names) = index.get(&id) { names } else { &[] } + } + + pub fn get_match(index: &HashMap<usize, Vec<RefName>>, id: usize) -> &[RefName] { + match index.get(&id) { + Some(names) => names, + None => &[], + } + } +} + 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 2d01b8ceaaa..cdbe51a4121 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -284,4 +284,20 @@ fn implicit_deref_ref() { }; } +mod issue_13018 { + use std::collections::HashMap; + + type RefName = i32; + pub fn get(index: &HashMap<usize, Vec<RefName>>, id: usize) -> &[RefName] { + if let Some(names) = index.get(&id) { names } else { &[] } + } + + pub fn get_match(index: &HashMap<usize, Vec<RefName>>, id: usize) -> &[RefName] { + match index.get(&id) { + Some(names) => names, + None => &[], + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed deleted file mode 100644 index 09e960ddd6a..00000000000 --- a/src/tools/clippy/tests/ui/match_same_arms2.fixed +++ /dev/null @@ -1,258 +0,0 @@ -#![warn(clippy::match_same_arms)] -#![allow( - clippy::disallowed_names, - clippy::diverging_sub_expression, - clippy::uninlined_format_args, - clippy::match_single_binding, - clippy::match_like_matches_macro -)] -fn bar<T>(_: T) {} -fn foo() -> bool { - unimplemented!() -} - -fn match_same_arms() { - let _ = match 42 { - _ => { - foo(); - let mut a = 42 + [23].len() as i32; - if true { - a += 7; - } - a = -31 - a; - a - }, - }; - //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm - - let _ = match 42 { - 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm - _ => true, - }; - - let _ = match Some(42) { - None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm - }; - - let _ = match Some(42) { - Some(foo) => 24, - None => 24, - }; - - let _ = match Some(42) { - Some(42) => 24, - Some(a) => 24, // bindings are different - None => 0, - }; - - let _ = match Some(42) { - Some(a) if a > 0 => 24, - Some(a) => 24, // one arm has a guard - None => 0, - }; - - match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm - _ => (), - } - - // No warning because guards are different - let _ = match Some(42) { - Some(a) if a == 42 => a, - Some(a) if a == 24 => a, - Some(_) => 24, - None => 0, - }; - - let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm - _ => 0, - }; - - match (Some(42), Some(42)) { - (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm - _ => (), - } - - let _ = match Some(()) { - Some(()) => 0.0, - None => -0.0, - }; - - match (Some(42), Some("")) { - (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), // bindings have different types - _ => (), - } - - let x: Result<i32, &str> = Ok(3); - - // No warning because of the guard. - match x { - Ok(x) if x * x == 64 => println!("ok"), - Ok(_) => println!("ok"), - Err(_) => println!("err"), - } - - // This used to be a false positive; see issue #1996. - match x { - Ok(3) => println!("ok"), - Ok(x) if x * x == 64 => println!("ok 64"), - Ok(_) => println!("ok"), - Err(_) => println!("err"), - } - - match (x, Some(1i32)) { - (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm - _ => println!("err"), - } - - // No warning; different types for `x`. - match (x, Some(1.0f64)) { - (Ok(x), Some(_)) => println!("ok {}", x), - (Ok(_), Some(x)) => println!("ok {}", x), - _ => println!("err"), - } - - // False negative #2251. - match x { - Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm - Err(_) => { - unreachable!(); - }, - } - - // False positive #1390 - macro_rules! empty { - ($e:expr) => {}; - } - match 0 { - 0 => { - empty!(0); - }, - 1 => { - empty!(1); - }, - x => { - empty!(x); - }, - }; - - // still lint if the tokens are the same - match 0 { - 1 | 0 => { - empty!(0); - }, - x => { - empty!(x); - }, - } - //~^^^^^^^ ERROR: this match arm has an identical body to another arm - - match_expr_like_matches_macro_priority(); -} - -fn match_expr_like_matches_macro_priority() { - enum E { - A, - B, - C, - } - let x = E::A; - let _ans = match x { - E::A => false, - E::B => false, - _ => true, - }; -} - -fn main() { - let _ = match Some(0) { - Some(0) => 0, - Some(1) => 1, - #[cfg(feature = "foo")] - Some(2) => 2, - _ => 1, - }; - - enum Foo { - X(u32), - Y(u32), - Z(u32), - } - - // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. - let _ = match Foo::X(0) { - Foo::X(0) => 1, - Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) => 1, - _ => 0, - }; - - // Suggest moving `Foo::Z(_)` up. - let _ = match Foo::X(0) { - Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm - Foo::X(_) | Foo::Y(_) => 2, - _ => 0, - }; - - // Suggest moving `Foo::X(0)` down. - let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm - _ => 0, - }; - - // Don't lint. - let _ = match 0 { - -2 => 1, - -5..=50 => 2, - -150..=88 => 1, - _ => 3, - }; - - struct Bar { - x: u32, - y: u32, - z: u32, - } - - // Lint. - let _ = match None { - Some(Bar { y: 10, z: 0, .. }) => 2, - None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm - _ => 200, - }; - - let _ = match 0 { - 0 => todo!(), - 1 => todo!(), - 2 => core::convert::identity::<u32>(todo!()), - 3 => core::convert::identity::<u32>(todo!()), - _ => 5, - }; - - let _ = match 0 { - 1 | 0 => cfg!(not_enable), - _ => false, - }; -} - -// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 -mod with_lifetime { - enum MaybeStaticStr<'a> { - Static(&'static str), - Borrowed(&'a str), - } - - impl<'a> MaybeStaticStr<'a> { - fn get(&self) -> &'a str { - match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, - //~^ ERROR: this match arm has an identical body to another arm - } - } - } -} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index cc7425135cc..dedd02e7873 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -6,6 +6,9 @@ clippy::match_single_binding, clippy::match_like_matches_macro )] + +//@no-rustfix: need to change the suggestion to a multipart suggestion + fn bar<T>(_: T) {} fn foo() -> bool { unimplemented!() diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index a5d137c658b..3a28b5afc2b 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:16:9 + --> tests/ui/match_same_arms2.rs:19:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | _ => { | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:25:9 + --> tests/ui/match_same_arms2.rs:28:9 | LL | / _ => { LL | | foo(); @@ -26,7 +26,7 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:39:9 + --> tests/ui/match_same_arms2.rs:42:9 | LL | 51 => foo(), | ^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL - 42 => foo(), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:45:9 + --> tests/ui/match_same_arms2.rs:48:9 | LL | None => 24, | ^^^^^^^^^^ @@ -58,7 +58,7 @@ LL - Some(_) => 24, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:67:9 + --> tests/ui/match_same_arms2.rs:70:9 | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL - (Some(a), None) => bar(a), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:81:9 + --> tests/ui/match_same_arms2.rs:84:9 | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL - (Some(a), None) if a == 42 => a, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:86:9 + --> tests/ui/match_same_arms2.rs:89:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL - (.., Some(a)) => bar(a), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:120:9 + --> tests/ui/match_same_arms2.rs:123:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL - (Ok(_), Some(x)) => println!("ok {}", x), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:136:9 + --> tests/ui/match_same_arms2.rs:139:9 | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +138,7 @@ LL - Ok(3) => println!("ok"), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:163:9 + --> tests/ui/match_same_arms2.rs:166:9 | LL | / 1 => { LL | | empty!(0); @@ -158,7 +158,7 @@ LL - }, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:214:9 + --> tests/ui/match_same_arms2.rs:217:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL - Foo::Z(_) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:224:9 + --> tests/ui/match_same_arms2.rs:227:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL - Foo::X(0) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:247:9 + --> tests/ui/match_same_arms2.rs:250:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +206,7 @@ LL - Some(Bar { x: 0, y: 5, .. }) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:261:9 + --> tests/ui/match_same_arms2.rs:264:9 | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL - 0 => cfg!(not_enable), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:277:17 + --> tests/ui/match_same_arms2.rs:280:17 | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/min_ident_chars.stderr b/src/tools/clippy/tests/ui/min_ident_chars.stderr index 7fbd8462fdc..3dd5c9561fd 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.stderr +++ b/src/tools/clippy/tests/ui/min_ident_chars.stderr @@ -193,11 +193,5 @@ error: this ident consists of a single char LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ -error: this ident consists of a single char - --> tests/ui/min_ident_chars.rs:93:41 - | -LL | struct Array<T, const N: usize>([T; N]); - | ^ - -error: aborting due to 33 previous errors +error: aborting due to 32 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index dbd739eee13..f54503ada97 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -200,7 +200,7 @@ mod with_ty_alias { } // NOTE: When checking the type of a function param, make sure it is not an alias with // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type - // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated + // is. Because the associate ty could have no default, therefore would cause ICE, as demonstrated // in this test. const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 4ac56f4c803..2c229068e4d 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -200,7 +200,7 @@ mod with_ty_alias { } // NOTE: When checking the type of a function param, make sure it is not an alias with // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type - // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated + // is. Because the associate ty could have no default, therefore would cause ICE, as demonstrated // in this test. fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} } diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed index 58f56eba0f5..84eaf12fc13 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed @@ -52,3 +52,21 @@ struct S<'a> { fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { s.opt.as_deref_mut() } + +mod issue_non_copy_13077 { + pub fn something(mut maybe_side_effect: Option<&mut String>) { + for _ in 0..10 { + let _ = S { + field: other(maybe_side_effect.as_deref_mut()), + }; + } + } + + fn other(_maybe_side_effect: Option<&mut String>) { + unimplemented!() + } + + pub struct S { + pub field: (), + } +} diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs index 842e025f669..fff1e45d846 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.rs +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs @@ -52,3 +52,21 @@ struct S<'a> { fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { s.opt.as_deref_mut() } + +mod issue_non_copy_13077 { + pub fn something(mut maybe_side_effect: Option<&mut String>) { + for _ in 0..10 { + let _ = S { + field: other(maybe_side_effect.as_deref_mut()), + }; + } + } + + fn other(_maybe_side_effect: Option<&mut String>) { + unimplemented!() + } + + pub struct S { + pub field: (), + } +} diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index c76f7a81843..7452eb77688 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -318,4 +318,51 @@ fn host_effect() { Add::<i32>::add(1, 1).add(i32::MIN); } +mod issue_10228 { + struct Entry; + + impl Entry { + fn or_insert(self, _default: i32) {} + fn or_default(self) { + // Don't lint, suggested code is an infinite recursion + self.or_insert(Default::default()) + } + } +} + +// issue #12973 +fn fn_call_in_nested_expr() { + struct Foo { + val: String, + } + + fn f() -> i32 { + 1 + } + let opt: Option<i32> = Some(1); + + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(f); // suggest `.unwrap_or_else(f)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(|| f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or_else(|| { + let x = f(); + x + 1 + }); + //~v ERROR: use of `map_or` followed by a function call + let _ = opt.map_or_else(|| f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + // + //~v ERROR: use of `unwrap_or` to construct default value + let _ = opt.unwrap_or_default(); + + let opt_foo = Some(Foo { + val: String::from("123"), + }); + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 97cf496d3ac..cd6f7bb2070 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -318,4 +318,51 @@ fn host_effect() { Add::<i32>::add(1, 1).add(i32::MIN); } +mod issue_10228 { + struct Entry; + + impl Entry { + fn or_insert(self, _default: i32) {} + fn or_default(self) { + // Don't lint, suggested code is an infinite recursion + self.or_insert(Default::default()) + } + } +} + +// issue #12973 +fn fn_call_in_nested_expr() { + struct Foo { + val: String, + } + + fn f() -> i32 { + 1 + } + let opt: Option<i32> = Some(1); + + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + // + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt.unwrap_or({ + let x = f(); + x + 1 + }); + //~v ERROR: use of `map_or` followed by a function call + let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + // + //~v ERROR: use of `unwrap_or` to construct default value + let _ = opt.unwrap_or({ i32::default() }); + + let opt_foo = Some(Foo { + val: String::from("123"), + }); + //~v ERROR: use of `unwrap_or` followed by a function call + let _ = opt_foo.unwrap_or(Foo { val: String::default() }); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 3070db22fc5..06f804fb41e 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -196,5 +196,53 @@ error: use of `unwrap_or_else` to construct default value LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: aborting due to 32 previous errors +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:345:17 + | +LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:348:17 + | +LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:351:17 + | +LL | let _ = opt.unwrap_or({ + | _________________^ +LL | | let x = f(); +LL | | x + 1 +LL | | }); + | |______^ + | +help: try + | +LL ~ let _ = opt.unwrap_or_else(|| { +LL + let x = f(); +LL + x + 1 +LL ~ }); + | + +error: use of `map_or` followed by a function call + --> tests/ui/or_fun_call.rs:356:17 + | +LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:359:17 + | +LL | let _ = opt.unwrap_or({ i32::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> tests/ui/or_fun_call.rs:365:21 + | +LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` + +error: aborting due to 38 previous errors diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed index 86e3e5bbdaf..1a46d72378f 100644 --- a/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed @@ -1,6 +1,7 @@ use std::path::PathBuf; -#[warn(clippy::all, clippy::path_buf_push_overwrite)] +#[warn(clippy::path_buf_push_overwrite)] +#[allow(clippy::pathbuf_init_then_push)] fn main() { let mut x = PathBuf::from("/foo"); x.push("bar"); diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs index 460cc254e94..3e3f84b17a4 100644 --- a/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; -#[warn(clippy::all, clippy::path_buf_push_overwrite)] +#[warn(clippy::path_buf_push_overwrite)] +#[allow(clippy::pathbuf_init_then_push)] fn main() { let mut x = PathBuf::from("/foo"); x.push("/bar"); diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr index c9f36ecf48f..48334235d11 100644 --- a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr @@ -1,5 +1,5 @@ error: calling `push` with '/' or '\' (file system root) will overwrite the previous path definition - --> tests/ui/path_buf_push_overwrite.rs:6:12 + --> tests/ui/path_buf_push_overwrite.rs:7:12 | LL | x.push("/bar"); | ^^^^^^ help: try: `"bar"` diff --git a/src/tools/clippy/tests/ui/pathbuf_init_then_push.fixed b/src/tools/clippy/tests/ui/pathbuf_init_then_push.fixed new file mode 100644 index 00000000000..2a90b8ce281 --- /dev/null +++ b/src/tools/clippy/tests/ui/pathbuf_init_then_push.fixed @@ -0,0 +1,22 @@ +#![warn(clippy::pathbuf_init_then_push)] + +use std::path::PathBuf; + +fn main() { + let mut path_buf = PathBuf::from("foo"); + + path_buf = PathBuf::from("foo").join("bar"); + + let bar = "bar"; + path_buf = PathBuf::from("foo").join(bar); + + let mut path_buf = PathBuf::from("foo").join("bar").join("buz"); + + let mut x = PathBuf::new(); + println!("{}", x.display()); + x.push("Duck"); + + let mut path_buf = PathBuf::new(); + #[cfg(cats)] + path_buf.push("foo"); +} diff --git a/src/tools/clippy/tests/ui/pathbuf_init_then_push.rs b/src/tools/clippy/tests/ui/pathbuf_init_then_push.rs new file mode 100644 index 00000000000..4a7ae00a735 --- /dev/null +++ b/src/tools/clippy/tests/ui/pathbuf_init_then_push.rs @@ -0,0 +1,26 @@ +#![warn(clippy::pathbuf_init_then_push)] + +use std::path::PathBuf; + +fn main() { + let mut path_buf = PathBuf::new(); //~ ERROR: calls to `push` immediately after creation + path_buf.push("foo"); + + path_buf = PathBuf::from("foo"); //~ ERROR: calls to `push` immediately after creation + path_buf.push("bar"); + + let bar = "bar"; + path_buf = PathBuf::from("foo"); //~ ERROR: calls to `push` immediately after creation + path_buf.push(bar); + + let mut path_buf = PathBuf::from("foo").join("bar"); //~ ERROR: calls to `push` immediately after creation + path_buf.push("buz"); + + let mut x = PathBuf::new(); + println!("{}", x.display()); + x.push("Duck"); + + let mut path_buf = PathBuf::new(); + #[cfg(cats)] + path_buf.push("foo"); +} diff --git a/src/tools/clippy/tests/ui/pathbuf_init_then_push.stderr b/src/tools/clippy/tests/ui/pathbuf_init_then_push.stderr new file mode 100644 index 00000000000..e7aa291035d --- /dev/null +++ b/src/tools/clippy/tests/ui/pathbuf_init_then_push.stderr @@ -0,0 +1,33 @@ +error: calls to `push` immediately after creation + --> tests/ui/pathbuf_init_then_push.rs:6:5 + | +LL | / let mut path_buf = PathBuf::new(); +LL | | path_buf.push("foo"); + | |_________________________^ help: consider using the `.join()`: `let mut path_buf = PathBuf::from("foo");` + | + = note: `-D clippy::pathbuf-init-then-push` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pathbuf_init_then_push)]` + +error: calls to `push` immediately after creation + --> tests/ui/pathbuf_init_then_push.rs:9:5 + | +LL | / path_buf = PathBuf::from("foo"); +LL | | path_buf.push("bar"); + | |_________________________^ help: consider using the `.join()`: `path_buf = PathBuf::from("foo").join("bar");` + +error: calls to `push` immediately after creation + --> tests/ui/pathbuf_init_then_push.rs:13:5 + | +LL | / path_buf = PathBuf::from("foo"); +LL | | path_buf.push(bar); + | |_______________________^ help: consider using the `.join()`: `path_buf = PathBuf::from("foo").join(bar);` + +error: calls to `push` immediately after creation + --> tests/ui/pathbuf_init_then_push.rs:16:5 + | +LL | / let mut path_buf = PathBuf::from("foo").join("bar"); +LL | | path_buf.push("buz"); + | |_________________________^ help: consider using the `.join()`: `let mut path_buf = PathBuf::from("foo").join("bar").join("buz");` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index 33c0725faad..21ac42196e1 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -46,6 +46,10 @@ fn main() { let _ = external!($ptr as *const u32); } +fn lifetime_to_static(v: *mut &()) -> *const &'static () { + v as _ +} + #[clippy::msrv = "1.64"] fn _msrv_1_64() { let ptr: *const u32 = &42_u32; diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index 24d959856db..5ce590b2b7e 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -46,6 +46,10 @@ fn main() { let _ = external!($ptr as *const u32); } +fn lifetime_to_static(v: *mut &()) -> *const &'static () { + v as _ +} + #[clippy::msrv = "1.64"] fn _msrv_1_64() { let ptr: *const u32 = &42_u32; diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 322c3585e62..2c52ebd3464 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -32,13 +32,13 @@ LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:64:13 + --> tests/ui/ptr_cast_constness.rs:68:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:65:13 + --> tests/ui/ptr_cast_constness.rs:69:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index 1f79b5e5360..1d04cca9b9e 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -3,6 +3,7 @@ #![allow( clippy::drop_non_drop, clippy::implicit_clone, + clippy::pathbuf_init_then_push, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap )] diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index 6909faebc99..738744fec98 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -3,6 +3,7 @@ #![allow( clippy::drop_non_drop, clippy::implicit_clone, + clippy::pathbuf_init_then_push, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap )] diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr index d66972bcb5b..3c37288f550 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.stderr +++ b/src/tools/clippy/tests/ui/redundant_clone.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/redundant_clone.rs:14:42 + --> tests/ui/redundant_clone.rs:15:42 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:14:14 + --> tests/ui/redundant_clone.rs:15:14 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,169 +13,169 @@ LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/redundant_clone.rs:17:15 + --> tests/ui/redundant_clone.rs:18:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:17:14 + --> tests/ui/redundant_clone.rs:18:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:20:15 + --> tests/ui/redundant_clone.rs:21:15 | LL | let _s = s.to_string(); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:20:14 + --> tests/ui/redundant_clone.rs:21:14 | LL | let _s = s.to_string(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:23:15 + --> tests/ui/redundant_clone.rs:24:15 | LL | let _s = s.to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:23:14 + --> tests/ui/redundant_clone.rs:24:14 | LL | let _s = s.to_owned(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:25:42 + --> tests/ui/redundant_clone.rs:26:42 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:25:14 + --> tests/ui/redundant_clone.rs:26:14 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:27:42 + --> tests/ui/redundant_clone.rs:28:42 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:27:14 + --> tests/ui/redundant_clone.rs:28:14 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:29:29 + --> tests/ui/redundant_clone.rs:30:29 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:29:14 + --> tests/ui/redundant_clone.rs:30:14 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:31:29 + --> tests/ui/redundant_clone.rs:32:29 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:31:14 + --> tests/ui/redundant_clone.rs:32:14 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:42:19 + --> tests/ui/redundant_clone.rs:43:19 | LL | let _t = tup.0.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:42:14 + --> tests/ui/redundant_clone.rs:43:14 | LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:74:25 + --> tests/ui/redundant_clone.rs:75:25 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:74:24 + --> tests/ui/redundant_clone.rs:75:24 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:131:15 + --> tests/ui/redundant_clone.rs:132:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:131:14 + --> tests/ui/redundant_clone.rs:132:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:132:15 + --> tests/ui/redundant_clone.rs:133:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:132:14 + --> tests/ui/redundant_clone.rs:133:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:142:19 + --> tests/ui/redundant_clone.rs:143:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:142:18 + --> tests/ui/redundant_clone.rs:143:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> tests/ui/redundant_clone.rs:154:14 + --> tests/ui/redundant_clone.rs:155:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> tests/ui/redundant_clone.rs:154:13 + --> tests/ui/redundant_clone.rs:155:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ error: redundant clone - --> tests/ui/redundant_clone.rs:208:11 + --> tests/ui/redundant_clone.rs:209:11 | LL | foo(&x.clone(), move || { | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/redundant_clone.rs:208:10 + --> tests/ui/redundant_clone.rs:209:10 | LL | foo(&x.clone(), move || { | ^ diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed deleted file mode 100644 index ed05f6e0c8d..00000000000 --- a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed +++ /dev/null @@ -1,144 +0,0 @@ -#![warn(clippy::significant_drop_tightening)] - -use std::sync::Mutex; - -pub fn complex_return_triggers_the_lint() -> i32 { - fn foo() -> i32 { - 1 - } - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let _ = *lock; - let _ = *lock; - drop(lock); - foo() -} - -pub fn issue_10413() { - let mutex = Mutex::new(Some(1)); - let opt = Some(1); - if opt.is_some() { - let lock = mutex.lock().unwrap(); - let _ = *lock; - if opt.is_some() { - let _ = *lock; - } - } -} - -pub fn issue_11128() { - use std::mem::drop as unlock; - - struct Foo { - droppable: Option<Vec<i32>>, - mutex: Mutex<Vec<i32>>, - } - - impl Drop for Foo { - fn drop(&mut self) { - if let Some(droppable) = self.droppable.take() { - let lock = self.mutex.lock().unwrap(); - let idx_opt = lock.iter().copied().find(|el| Some(el) == droppable.first()); - if let Some(idx) = idx_opt { - let local_droppable = vec![lock.first().copied().unwrap_or_default()]; - unlock(lock); - drop(local_droppable); - } - } - } - } -} - -pub fn issue_11160() -> bool { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - true -} - -pub fn issue_11189() { - struct Number { - pub value: u32, - } - - fn do_something() -> Result<(), ()> { - let number = Mutex::new(Number { value: 1 }); - let number2 = Mutex::new(Number { value: 2 }); - let number3 = Mutex::new(Number { value: 3 }); - let mut lock = number.lock().unwrap(); - let mut lock2 = number2.lock().unwrap(); - let mut lock3 = number3.lock().unwrap(); - lock.value += 1; - lock2.value += 1; - lock3.value += 1; - drop((lock, lock2, lock3)); - Ok(()) - } -} - -pub fn path_return_can_be_ignored() -> i32 { - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let rslt = *lock; - let _ = *lock; - rslt -} - -pub fn post_bindings_can_be_ignored() { - let mutex = Mutex::new(1); - let lock = mutex.lock().unwrap(); - let rslt = *lock; - let another = rslt; - let _ = another; -} - -pub fn unnecessary_contention_with_multiple_owned_results() { - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - let _ = lock.is_positive(); - } - - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let rslt0 = lock.abs(); - let rslt1 = lock.is_positive(); - drop(lock); - do_heavy_computation_that_takes_time((rslt0, rslt1)); - } -} - -pub fn unnecessary_contention_with_single_owned_results() { - { - let mutex = Mutex::new(1i32); - let lock = mutex.lock().unwrap(); - let _ = lock.abs(); - } - { - let mutex = Mutex::new(vec![1i32]); - let mut lock = mutex.lock().unwrap(); - lock.clear(); - } - - { - let mutex = Mutex::new(1i32); - - let rslt0 = mutex.lock().unwrap().abs(); - - do_heavy_computation_that_takes_time(rslt0); - } - { - let mutex = Mutex::new(vec![1i32]); - - mutex.lock().unwrap().clear(); - - do_heavy_computation_that_takes_time(()); - } -} - -// Marker used for illustration purposes. -pub fn do_heavy_computation_that_takes_time<T>(_: T) {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.rs b/src/tools/clippy/tests/ui/significant_drop_tightening.rs index e5f17278f0f..77538167548 100644 --- a/src/tools/clippy/tests/ui/significant_drop_tightening.rs +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.rs @@ -1,5 +1,7 @@ #![warn(clippy::significant_drop_tightening)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::sync::Mutex; pub fn complex_return_triggers_the_lint() -> i32 { diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr index 5fc66279f00..2d7da4f394d 100644 --- a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:10:9 + --> tests/ui/significant_drop_tightening.rs:12:9 | LL | pub fn complex_return_triggers_the_lint() -> i32 { | __________________________________________________- @@ -24,7 +24,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:104:13 + --> tests/ui/significant_drop_tightening.rs:106:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -44,7 +44,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:125:13 + --> tests/ui/significant_drop_tightening.rs:127:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -67,7 +67,7 @@ LL - let rslt0 = lock.abs(); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:131:17 + --> tests/ui/significant_drop_tightening.rs:133:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); diff --git a/src/tools/clippy/tests/ui/single_element_loop.fixed b/src/tools/clippy/tests/ui/single_element_loop.fixed index 4e59c763198..64cbd5e9c90 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.fixed +++ b/src/tools/clippy/tests/ui/single_element_loop.fixed @@ -57,4 +57,12 @@ fn main() { } }; } + + // Should lint with correct suggestion (issue #12782) + let res_void: Result<bool, bool> = Ok(true); + + { + let (Ok(mut _x) | Err(mut _x)) = res_void; + let ptr: *const bool = std::ptr::null(); + } } diff --git a/src/tools/clippy/tests/ui/single_element_loop.rs b/src/tools/clippy/tests/ui/single_element_loop.rs index a55ece6b065..92406f1c1ca 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.rs +++ b/src/tools/clippy/tests/ui/single_element_loop.rs @@ -54,4 +54,11 @@ fn main() { } }; } + + // Should lint with correct suggestion (issue #12782) + let res_void: Result<bool, bool> = Ok(true); + + for (Ok(mut _x) | Err(mut _x)) in [res_void] { + let ptr: *const bool = std::ptr::null(); + } } diff --git a/src/tools/clippy/tests/ui/single_element_loop.stderr b/src/tools/clippy/tests/ui/single_element_loop.stderr index dfae5605b57..73453dd2dfd 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.stderr +++ b/src/tools/clippy/tests/ui/single_element_loop.stderr @@ -83,5 +83,21 @@ LL + }; LL + } | -error: aborting due to 7 previous errors +error: for loop over a single element + --> tests/ui/single_element_loop.rs:61:5 + | +LL | / for (Ok(mut _x) | Err(mut _x)) in [res_void] { +LL | | let ptr: *const bool = std::ptr::null(); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let (Ok(mut _x) | Err(mut _x)) = res_void; +LL + let ptr: *const bool = std::ptr::null(); +LL + } + | + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed index ff5cd7abbb6..3eb47a5b5fd 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed @@ -36,4 +36,6 @@ fn main() { // do not lint in external macro external!(let ref _y = 42;); + + fn f(#[allow(clippy::toplevel_ref_arg)] ref x: i32) {} } diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs index ab79b895960..cd731387de9 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs @@ -36,4 +36,6 @@ fn main() { // do not lint in external macro external!(let ref _y = 42;); + + fn f(#[allow(clippy::toplevel_ref_arg)] ref x: i32) {} } diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed index b696a574ae3..39e67b25052 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed @@ -1,17 +1,19 @@ #![warn(clippy::transmute_ptr_to_ptr)] #![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] +use std::mem::transmute; + // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - std::mem::transmute::<&'a T, &'static T>(t) + transmute::<&'a T, &'static T>(t) } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - std::mem::transmute::<&'a T, &'b T>(t) + transmute::<&'a T, &'b T>(t) } struct LifetimeParam<'a> { @@ -27,39 +29,40 @@ fn transmute_ptr_to_ptr() { let mut_ptr = &mut 1u32 as *mut u32; unsafe { // pointer-to-pointer transmutes; bad - let _: *const f32 = ptr as *const f32; - //~^ ERROR: transmute from a pointer to a pointer - //~| NOTE: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` - let _: *mut f32 = mut_ptr as *mut f32; - //~^ ERROR: transmute from a pointer to a pointer + let _: *const f32 = ptr.cast::<f32>(); + //~^ transmute_ptr_to_ptr + let _: *mut f32 = mut_ptr.cast::<f32>(); + //~^ transmute_ptr_to_ptr // ref-ref transmutes; bad let _: &f32 = &*(&1u32 as *const u32 as *const f32); - //~^ ERROR: transmute from a reference to a reference + //~^ transmute_ptr_to_ptr let _: &f32 = &*(&1f64 as *const f64 as *const f32); - //~^ ERROR: transmute from a reference to a reference + //~^ transmute_ptr_to_ptr //:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not // the same type let _: &mut f32 = &mut *(&mut 1u32 as *mut u32 as *mut f32); - //~^ ERROR: transmute from a reference to a reference + //~^ transmute_ptr_to_ptr let _: &GenericParam<f32> = &*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>); - //~^ ERROR: transmute from a reference to a reference + //~^ transmute_ptr_to_ptr let u64_ref: &u64 = &0u64; - let u8_ref: &u8 = unsafe { &*(u64_ref as *const u64 as *const u8) }; - //~^ ERROR: transmute from a reference to a reference + let u8_ref: &u8 = &*(u64_ref as *const u64 as *const u8); + //~^ transmute_ptr_to_ptr + let _: *const u32 = mut_ptr.cast_const(); + //~^ transmute_ptr_to_ptr + let _: *mut u32 = ptr.cast_mut(); + //~^ transmute_ptr_to_ptr } - // these are recommendations for solving the above; if these lint we need to update - // those suggestions - let _ = ptr as *const f32; - let _ = mut_ptr as *mut f32; - let _ = unsafe { &*(&1u32 as *const u32 as *const f32) }; - let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) }; - // transmute internal lifetimes, should not lint let s = "hello world".to_owned(); let lp = LifetimeParam { s: &s }; - let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) }; - let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; + let _: &LifetimeParam<'static> = unsafe { transmute(&lp) }; + let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) }; +} + +fn lifetime_to_static(v: *mut &()) -> *const &'static () { + unsafe { v as *const &() } + //~^ transmute_ptr_to_ptr } // dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) @@ -67,7 +70,37 @@ const _: &() = { struct Zst; let zst = &Zst; - unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) } + unsafe { transmute::<&'static Zst, &'static ()>(zst) } }; +#[clippy::msrv = "1.37"] +fn msrv_1_37(ptr: *const u8) { + unsafe { + let _: *const i8 = ptr as *const i8; + } +} + +#[clippy::msrv = "1.38"] +fn msrv_1_38(ptr: *const u8) { + unsafe { + let _: *const i8 = ptr.cast::<i8>(); + } +} + +#[clippy::msrv = "1.64"] +fn msrv_1_64(ptr: *const u8, mut_ptr: *mut u8) { + unsafe { + let _: *mut u8 = ptr as *mut u8; + let _: *const u8 = mut_ptr as *const u8; + } +} + +#[clippy::msrv = "1.65"] +fn msrv_1_65(ptr: *const u8, mut_ptr: *mut u8) { + unsafe { + let _: *mut u8 = ptr.cast_mut(); + let _: *const u8 = mut_ptr.cast_const(); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs index 85cc1d7802c..580b2855173 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -1,17 +1,19 @@ #![warn(clippy::transmute_ptr_to_ptr)] #![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] +use std::mem::transmute; + // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - std::mem::transmute::<&'a T, &'static T>(t) + transmute::<&'a T, &'static T>(t) } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - std::mem::transmute::<&'a T, &'b T>(t) + transmute::<&'a T, &'b T>(t) } struct LifetimeParam<'a> { @@ -27,39 +29,40 @@ fn transmute_ptr_to_ptr() { let mut_ptr = &mut 1u32 as *mut u32; unsafe { // pointer-to-pointer transmutes; bad - let _: *const f32 = std::mem::transmute(ptr); - //~^ ERROR: transmute from a pointer to a pointer - //~| NOTE: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` - let _: *mut f32 = std::mem::transmute(mut_ptr); - //~^ ERROR: transmute from a pointer to a pointer + let _: *const f32 = transmute(ptr); + //~^ transmute_ptr_to_ptr + let _: *mut f32 = transmute(mut_ptr); + //~^ transmute_ptr_to_ptr // ref-ref transmutes; bad - let _: &f32 = std::mem::transmute(&1u32); - //~^ ERROR: transmute from a reference to a reference - let _: &f32 = std::mem::transmute(&1f64); - //~^ ERROR: transmute from a reference to a reference + let _: &f32 = transmute(&1u32); + //~^ transmute_ptr_to_ptr + let _: &f32 = transmute(&1f64); + //~^ transmute_ptr_to_ptr //:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not // the same type - let _: &mut f32 = std::mem::transmute(&mut 1u32); - //~^ ERROR: transmute from a reference to a reference - let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); - //~^ ERROR: transmute from a reference to a reference + let _: &mut f32 = transmute(&mut 1u32); + //~^ transmute_ptr_to_ptr + let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 }); + //~^ transmute_ptr_to_ptr let u64_ref: &u64 = &0u64; - let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) }; - //~^ ERROR: transmute from a reference to a reference + let u8_ref: &u8 = transmute(u64_ref); + //~^ transmute_ptr_to_ptr + let _: *const u32 = transmute(mut_ptr); + //~^ transmute_ptr_to_ptr + let _: *mut u32 = transmute(ptr); + //~^ transmute_ptr_to_ptr } - // these are recommendations for solving the above; if these lint we need to update - // those suggestions - let _ = ptr as *const f32; - let _ = mut_ptr as *mut f32; - let _ = unsafe { &*(&1u32 as *const u32 as *const f32) }; - let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) }; - // transmute internal lifetimes, should not lint let s = "hello world".to_owned(); let lp = LifetimeParam { s: &s }; - let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) }; - let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; + let _: &LifetimeParam<'static> = unsafe { transmute(&lp) }; + let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) }; +} + +fn lifetime_to_static(v: *mut &()) -> *const &'static () { + unsafe { transmute(v) } + //~^ transmute_ptr_to_ptr } // dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) @@ -67,7 +70,37 @@ const _: &() = { struct Zst; let zst = &Zst; - unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) } + unsafe { transmute::<&'static Zst, &'static ()>(zst) } }; +#[clippy::msrv = "1.37"] +fn msrv_1_37(ptr: *const u8) { + unsafe { + let _: *const i8 = transmute(ptr); + } +} + +#[clippy::msrv = "1.38"] +fn msrv_1_38(ptr: *const u8) { + unsafe { + let _: *const i8 = transmute(ptr); + } +} + +#[clippy::msrv = "1.64"] +fn msrv_1_64(ptr: *const u8, mut_ptr: *mut u8) { + unsafe { + let _: *mut u8 = transmute(ptr); + let _: *const u8 = transmute(mut_ptr); + } +} + +#[clippy::msrv = "1.65"] +fn msrv_1_65(ptr: *const u8, mut_ptr: *mut u8) { + unsafe { + let _: *mut u8 = transmute(ptr); + let _: *const u8 = transmute(mut_ptr); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr index cd1f1f398dc..8801eb943ce 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,47 +1,155 @@ error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:30:29 + --> tests/ui/transmute_ptr_to_ptr.rs:32:29 | -LL | let _: *const f32 = std::mem::transmute(ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` +LL | let _: *const f32 = transmute(ptr); + | ^^^^^^^^^^^^^^ | = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]` +help: use `pointer::cast` instead + | +LL | let _: *const f32 = ptr.cast::<f32>(); + | ~~~~~~~~~~~~~~~~~ error: transmute from a pointer to a pointer - --> tests/ui/transmute_ptr_to_ptr.rs:33:27 + --> tests/ui/transmute_ptr_to_ptr.rs:34:27 + | +LL | let _: *mut f32 = transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast` instead | -LL | let _: *mut f32 = std::mem::transmute(mut_ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` +LL | let _: *mut f32 = mut_ptr.cast::<f32>(); + | ~~~~~~~~~~~~~~~~~~~~~ error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:36:23 + --> tests/ui/transmute_ptr_to_ptr.rs:37:23 | -LL | let _: &f32 = std::mem::transmute(&1u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` +LL | let _: &f32 = transmute(&1u32); + | ^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:38:23 + --> tests/ui/transmute_ptr_to_ptr.rs:39:23 | -LL | let _: &f32 = std::mem::transmute(&1f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)` +LL | let _: &f32 = transmute(&1f64); + | ^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:42:27 + --> tests/ui/transmute_ptr_to_ptr.rs:43:27 | -LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` +LL | let _: &mut f32 = transmute(&mut 1u32); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:44:37 + --> tests/ui/transmute_ptr_to_ptr.rs:45:37 | -LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)` +LL | let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)` error: transmute from a reference to a reference - --> tests/ui/transmute_ptr_to_ptr.rs:47:36 + --> tests/ui/transmute_ptr_to_ptr.rs:48:27 + | +LL | let u8_ref: &u8 = transmute(u64_ref); + | ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)` + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:50:29 + | +LL | let _: *const u32 = transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast_const` instead + | +LL | let _: *const u32 = mut_ptr.cast_const(); + | ~~~~~~~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:52:27 + | +LL | let _: *mut u32 = transmute(ptr); + | ^^^^^^^^^^^^^^ + | +help: use `pointer::cast_mut` instead + | +LL | let _: *mut u32 = ptr.cast_mut(); + | ~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:64:14 + | +LL | unsafe { transmute(v) } + | ^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL | unsafe { v as *const &() } + | ~~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:79:28 + | +LL | let _: *const i8 = transmute(ptr); + | ^^^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL | let _: *const i8 = ptr as *const i8; + | ~~~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:86:28 + | +LL | let _: *const i8 = transmute(ptr); + | ^^^^^^^^^^^^^^ + | +help: use `pointer::cast` instead + | +LL | let _: *const i8 = ptr.cast::<i8>(); + | ~~~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:93:26 + | +LL | let _: *mut u8 = transmute(ptr); + | ^^^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL | let _: *mut u8 = ptr as *mut u8; + | ~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:94:28 + | +LL | let _: *const u8 = transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL | let _: *const u8 = mut_ptr as *const u8; + | ~~~~~~~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:101:26 + | +LL | let _: *mut u8 = transmute(ptr); + | ^^^^^^^^^^^^^^ + | +help: use `pointer::cast_mut` instead + | +LL | let _: *mut u8 = ptr.cast_mut(); + | ~~~~~~~~~~~~~~ + +error: transmute from a pointer to a pointer + --> tests/ui/transmute_ptr_to_ptr.rs:102:28 + | +LL | let _: *const u8 = transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^ + | +help: use `pointer::cast_const` instead | -LL | let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)` +LL | let _: *const u8 = mut_ptr.cast_const(); + | ~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 7 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 51682da4a98..e95054a7ccb 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -18,7 +18,7 @@ fn main() { let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 }; + let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() }; let _ptr_i8 = ptr_i32 as *const i8; let slice_ptr = &[0, 1, 2, 3] as *const [i32]; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 2ca44485826..2d74967ede5 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -11,16 +11,25 @@ error: transmute from a pointer to a pointer --> tests/ui/transmutes_expressible_as_ptr_casts.rs:21:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]` +help: use `pointer::cast` instead + | +LL | let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() }; + | ~~~~~~~~~~~~~~~~~~~~ error: transmute from a pointer to a pointer --> tests/ui/transmutes_expressible_as_ptr_casts.rs:27:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u32]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use an `as` cast instead + | +LL | let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead --> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50 diff --git a/src/tools/clippy/tests/ui/types.fixed b/src/tools/clippy/tests/ui/types.fixed deleted file mode 100644 index 6f1f55f0e62..00000000000 --- a/src/tools/clippy/tests/ui/types.fixed +++ /dev/null @@ -1,13 +0,0 @@ -#![allow(dead_code, unused_variables)] -#![warn(clippy::cast_lossless)] - -// should not warn on lossy casting in constant types -// because not supported yet -const C: i32 = 42; -const C_I64: i64 = C as i64; - -fn main() { - // should suggest i64::from(c) - let c: i32 = 42; - let c_i64: i64 = i64::from(c); -} diff --git a/src/tools/clippy/tests/ui/types.rs b/src/tools/clippy/tests/ui/types.rs deleted file mode 100644 index 960aee4600c..00000000000 --- a/src/tools/clippy/tests/ui/types.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![allow(dead_code, unused_variables)] -#![warn(clippy::cast_lossless)] - -// should not warn on lossy casting in constant types -// because not supported yet -const C: i32 = 42; -const C_I64: i64 = C as i64; - -fn main() { - // should suggest i64::from(c) - let c: i32 = 42; - let c_i64: i64 = c as i64; -} diff --git a/src/tools/clippy/tests/ui/types.stderr b/src/tools/clippy/tests/ui/types.stderr deleted file mode 100644 index 02e75018129..00000000000 --- a/src/tools/clippy/tests/ui/types.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: casting `i32` to `i64` may become silently lossy if you later change the type - --> tests/ui/types.rs:12:22 - | -LL | let c_i64: i64 = c as i64; - | ^^^^^^^^ help: try: `i64::from(c)` - | - = note: `-D clippy::cast-lossless` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed deleted file mode 100644 index dc5e163ff04..00000000000 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed +++ /dev/null @@ -1,201 +0,0 @@ -#![allow(unused_assignments)] -#![warn(clippy::unnecessary_to_owned)] - -#[allow(dead_code)] -#[derive(Clone, Copy)] -enum FileType { - Account, - PrivateKey, - Certificate, -} - -fn main() { - let path = std::path::Path::new("x"); - - let _ = check_files(&[(FileType::Account, path)]); - let _ = check_files_vec(vec![(FileType::Account, path)]); - - // negative tests - let _ = check_files_ref(&[(FileType::Account, path)]); - let _ = check_files_mut(&[(FileType::Account, path)]); - let _ = check_files_ref_mut(&[(FileType::Account, path)]); - let _ = check_files_self_and_arg(&[(FileType::Account, path)]); - let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); - - check_mut_iteratee_and_modify_inner_variable(); -} - -// `check_files` and its variants are based on: -// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 -fn check_files(files: &[(FileType, &std::path::Path)]) -> bool { - for (t, path) in files { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool { - for (t, path) in files.iter() { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool { - for (ref t, path) in files.iter().copied() { - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -#[allow(unused_assignments)] -fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool { - for (mut t, path) in files.iter().copied() { - t = FileType::PrivateKey; - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool { - for (ref mut t, path) in files.iter().copied() { - *t = FileType::PrivateKey; - let other = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool { - for (t, path) in files.iter().copied() { - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.join(path).is_file() || !other.is_file() { - return false; - } - } - true -} - -#[allow(unused_assignments)] -fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { - for (mut t, path) in files.iter().cloned() { - t = FileType::PrivateKey; - let other = match get_file_path(&t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() || !other.is_file() { - return false; - } - } - true -} - -fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> { - Ok(std::path::PathBuf::new()) -} - -// Issue 12098 -// https://github.com/rust-lang/rust-clippy/issues/12098 -// no message emits -fn check_mut_iteratee_and_modify_inner_variable() { - struct Test { - list: Vec<String>, - mut_this: bool, - } - - impl Test { - fn list(&self) -> &[String] { - &self.list - } - } - - let mut test = Test { - list: vec![String::from("foo"), String::from("bar")], - mut_this: false, - }; - - for _item in test.list().to_vec() { - println!("{}", _item); - - test.mut_this = true; - { - test.mut_this = true; - } - } -} - -mod issue_12821 { - fn foo() { - let v: Vec<_> = "hello".chars().collect(); - for c in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - println!("{c}"); // should not suggest to remove `&` - } - } - - fn bar() { - let v: Vec<_> = "hello".chars().collect(); - for c in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - let ref_c = c; //~ HELP: remove any references to the binding - println!("{ref_c}"); - } - } - - fn baz() { - let v: Vec<_> = "hello".chars().enumerate().collect(); - for (i, c) in v.iter() { - //~^ ERROR: unnecessary use of `cloned` - let ref_c = c; //~ HELP: remove any references to the binding - let ref_i = i; - println!("{i} {ref_c}"); // should not suggest to remove `&` from `i` - } - } -} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index 8f797ac717f..331b7b25271 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -1,6 +1,8 @@ #![allow(unused_assignments)] #![warn(clippy::unnecessary_to_owned)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + #[allow(dead_code)] #[derive(Clone, Copy)] enum FileType { diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr index 0bdb37a521f..e3592e3cbbd 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:31:22 + --> tests/ui/unnecessary_iter_cloned.rs:33:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:46:22 + --> tests/ui/unnecessary_iter_cloned.rs:48:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -33,13 +33,13 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:177:18 + --> tests/ui/unnecessary_iter_cloned.rs:179:18 | LL | for c in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ help: use: `v.iter()` error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:185:18 + --> tests/ui/unnecessary_iter_cloned.rs:187:18 | LL | for c in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL + let ref_c = c; | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:194:23 + --> tests/ui/unnecessary_iter_cloned.rs:196:23 | LL | for (i, c) in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed index f3cf65da2d6..b0e8f454635 100644 --- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed @@ -26,6 +26,11 @@ struct V { f: u32, } +struct W { + f1: u32, + f2: u32, +} + impl Clone for V { fn clone(&self) -> Self { // Lint: `Self` implements `Copy` @@ -68,4 +73,69 @@ fn main() { // Should lint: the result of an expression is mutable and temporary let p = &mut *Box::new(T { f: 5 }); + + // Should lint: all fields of `q` would be consumed anyway + let q = W { f1: 42, f2: 1337 }; + let r = q; + + // Should not lint: not all fields of `t` from same source + let s = W { f1: 1337, f2: 42 }; + let t = W { f1: s.f1, f2: r.f2 }; + + // Should not lint: different fields of `s` assigned + let u = W { f1: s.f2, f2: s.f1 }; + + // Should lint: all fields of `v` would be consumed anyway + let v = W { f1: 42, f2: 1337 }; + let w = v; + + // Should not lint: source differs between fields and base + let x = W { f1: 42, f2: 1337 }; + let y = W { f1: w.f1, ..x }; + + // Should lint: range desugars to struct + let r1 = 0..5; + let r2 = r1; + + references(); + shorthand(); +} + +fn references() { + // Should not lint as `a` is not mutable + let a = W { f1: 42, f2: 1337 }; + let b = &mut W { f1: a.f1, f2: a.f2 }; + + // Should lint as `d` is a shared reference + let c = W { f1: 42, f2: 1337 }; + let d = &c; + + // Should not lint as `e` is not mutable + let e = W { f1: 42, f2: 1337 }; + let f = &mut W { f1: e.f1, ..e }; + + // Should lint as `h` is a shared reference + let g = W { f1: 42, f2: 1337 }; + let h = &g; + + // Should not lint as `j` is copy + let i = V { f: 0x1701d }; + let j = &V { ..i }; + + // Should not lint as `k` is copy + let k = V { f: 0x1701d }; + let l = &V { f: k.f }; +} + +fn shorthand() { + struct S1 { + a: i32, + b: i32, + } + + let a = 42; + let s = S1 { a: 3, b: 4 }; + + // Should not lint: `a` is not from `s` + let s = S1 { a, b: s.b }; } diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs index bd5302f9d85..b0db71af4d4 100644 --- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs @@ -26,6 +26,11 @@ struct V { f: u32, } +struct W { + f1: u32, + f2: u32, +} + impl Clone for V { fn clone(&self) -> Self { // Lint: `Self` implements `Copy` @@ -72,4 +77,69 @@ fn main() { let p = &mut T { ..*Box::new(T { f: 5 }) }; + + // Should lint: all fields of `q` would be consumed anyway + let q = W { f1: 42, f2: 1337 }; + let r = W { f1: q.f1, f2: q.f2 }; + + // Should not lint: not all fields of `t` from same source + let s = W { f1: 1337, f2: 42 }; + let t = W { f1: s.f1, f2: r.f2 }; + + // Should not lint: different fields of `s` assigned + let u = W { f1: s.f2, f2: s.f1 }; + + // Should lint: all fields of `v` would be consumed anyway + let v = W { f1: 42, f2: 1337 }; + let w = W { f1: v.f1, ..v }; + + // Should not lint: source differs between fields and base + let x = W { f1: 42, f2: 1337 }; + let y = W { f1: w.f1, ..x }; + + // Should lint: range desugars to struct + let r1 = 0..5; + let r2 = r1.start..r1.end; + + references(); + shorthand(); +} + +fn references() { + // Should not lint as `a` is not mutable + let a = W { f1: 42, f2: 1337 }; + let b = &mut W { f1: a.f1, f2: a.f2 }; + + // Should lint as `d` is a shared reference + let c = W { f1: 42, f2: 1337 }; + let d = &W { f1: c.f1, f2: c.f2 }; + + // Should not lint as `e` is not mutable + let e = W { f1: 42, f2: 1337 }; + let f = &mut W { f1: e.f1, ..e }; + + // Should lint as `h` is a shared reference + let g = W { f1: 42, f2: 1337 }; + let h = &W { f1: g.f1, ..g }; + + // Should not lint as `j` is copy + let i = V { f: 0x1701d }; + let j = &V { ..i }; + + // Should not lint as `k` is copy + let k = V { f: 0x1701d }; + let l = &V { f: k.f }; +} + +fn shorthand() { + struct S1 { + a: i32, + b: i32, + } + + let a = 42; + let s = S1 { a: 3, b: 4 }; + + // Should not lint: `a` is not from `s` + let s = S1 { a, b: s.b }; } diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr index 8bc308c7567..56982cc0a39 100644 --- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr @@ -1,5 +1,5 @@ error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:32:9 + --> tests/ui/unnecessary_struct_initialization.rs:37:9 | LL | Self { ..*self } | ^^^^^^^^^^^^^^^^ help: replace with: `*self` @@ -8,25 +8,25 @@ LL | Self { ..*self } = help: to override `-D warnings` add `#[allow(clippy::unnecessary_struct_initialization)]` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:39:17 + --> tests/ui/unnecessary_struct_initialization.rs:44:17 | LL | let mut b = S { ..a }; | ^^^^^^^^^ help: replace with: `a` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:42:18 + --> tests/ui/unnecessary_struct_initialization.rs:47:18 | LL | let c = &mut S { ..b }; | ^^^^^^^^^ help: replace with: `b` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:50:14 + --> tests/ui/unnecessary_struct_initialization.rs:55:14 | LL | let g = &S { ..f }; | ^^^^^^^^^ help: replace with: `f` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:53:18 + --> tests/ui/unnecessary_struct_initialization.rs:58:18 | LL | let h = &mut S { | __________________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: replace with: `*Box::new(S { f: String::from("foo") })` error: unnecessary struct building - --> tests/ui/unnecessary_struct_initialization.rs:72:18 + --> tests/ui/unnecessary_struct_initialization.rs:77:18 | LL | let p = &mut T { | __________________^ @@ -43,5 +43,35 @@ LL | | ..*Box::new(T { f: 5 }) LL | | }; | |_____^ help: replace with: `*Box::new(T { f: 5 })` -error: aborting due to 6 previous errors +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:83:13 + | +LL | let r = W { f1: q.f1, f2: q.f2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `q` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:94:13 + | +LL | let w = W { f1: v.f1, ..v }; + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `v` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:102:14 + | +LL | let r2 = r1.start..r1.end; + | ^^^^^^^^^^^^^^^^ help: replace with: `r1` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:115:14 + | +LL | let d = &W { f1: c.f1, f2: c.f2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `c` + +error: unnecessary struct building + --> tests/ui/unnecessary_struct_initialization.rs:123:14 + | +LL | let h = &W { f1: g.f1, ..g }; + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `g` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed deleted file mode 100644 index fdcac8fb08d..00000000000 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ /dev/null @@ -1,587 +0,0 @@ -#![allow( - clippy::needless_borrow, - clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, - clippy::needless_lifetimes -)] -#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] - -use std::borrow::Cow; -use std::ffi::{CStr, CString, OsStr, OsString}; -use std::ops::Deref; - -#[derive(Clone)] -struct X(String); - -impl Deref for X { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl AsRef<str> for X { - fn as_ref(&self) -> &str { - self.0.as_str() - } -} - -#[allow(clippy::to_string_trait_impl)] -impl ToString for X { - fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl X { - fn join(&self, other: impl AsRef<str>) -> Self { - let mut s = self.0.clone(); - s.push_str(other.as_ref()); - Self(s) - } -} - -#[allow(dead_code)] -#[derive(Clone)] -enum FileType { - Account, - PrivateKey, - Certificate, -} - -fn main() { - let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); - let os_str = OsStr::new("x"); - let path = std::path::Path::new("x"); - let s = "x"; - let array = ["x"]; - let array_ref = &["x"]; - let slice = &["x"][..]; - let x = X(String::from("x")); - let x_ref = &x; - - require_c_str(&Cow::from(c_str)); - require_c_str(c_str); - - require_os_str(os_str); - require_os_str(&Cow::from(os_str)); - require_os_str(os_str); - - require_path(path); - require_path(&Cow::from(path)); - require_path(path); - - require_str(s); - require_str(&Cow::from(s)); - require_str(s); - require_str(x_ref.as_ref()); - - require_slice(slice); - require_slice(&Cow::from(slice)); - require_slice(array.as_ref()); - require_slice(array_ref.as_ref()); - require_slice(slice); - require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. - - require_x(&Cow::<X>::Owned(x.clone())); - require_x(&x_ref.to_owned()); // No longer flagged because of #8759. - - require_deref_c_str(c_str); - require_deref_os_str(os_str); - require_deref_path(path); - require_deref_str(s); - require_deref_slice(slice); - - require_impl_deref_c_str(c_str); - require_impl_deref_os_str(os_str); - require_impl_deref_path(path); - require_impl_deref_str(s); - require_impl_deref_slice(slice); - - require_deref_str_slice(s, slice); - require_deref_slice_str(slice, s); - - require_as_ref_c_str(c_str); - require_as_ref_os_str(os_str); - require_as_ref_path(path); - require_as_ref_str(s); - require_as_ref_str(&x); - require_as_ref_slice(array); - require_as_ref_slice(array_ref); - require_as_ref_slice(slice); - - require_impl_as_ref_c_str(c_str); - require_impl_as_ref_os_str(os_str); - require_impl_as_ref_path(path); - require_impl_as_ref_str(s); - require_impl_as_ref_str(&x); - require_impl_as_ref_slice(array); - require_impl_as_ref_slice(array_ref); - require_impl_as_ref_slice(slice); - - require_as_ref_str_slice(s, array); - require_as_ref_str_slice(s, array_ref); - require_as_ref_str_slice(s, slice); - require_as_ref_slice_str(array, s); - require_as_ref_slice_str(array_ref, s); - require_as_ref_slice_str(slice, s); - - let _ = x.join(x_ref); - - let _ = slice.iter().copied(); - let _ = slice.iter().copied(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - - let _ = slice.iter().copied(); - let _ = slice.iter().copied(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - - let _ = check_files(&[FileType::Account]); - - // negative tests - require_string(&s.to_string()); - require_string(&Cow::from(s).into_owned()); - require_string(&s.to_owned()); - require_string(&x_ref.to_string()); - - // `X` isn't copy. - require_slice(&x.to_owned()); - require_deref_slice(x.to_owned()); - - // The following should be flagged by `redundant_clone`, but not by this lint. - require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap()); - require_os_str(&OsString::from("x")); - require_path(&std::path::PathBuf::from("x")); - require_str(&String::from("x")); - require_slice(&[String::from("x")]); - - let slice = [0u8; 1024]; - let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); - let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); - let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); - // Expression is of type `&String`, can't suggest `str::from_utf8` here - let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap(); - macro_rules! arg_from_macro { - () => { - b"foo".to_vec() - }; - } - macro_rules! string_from_utf8_from_macro { - () => { - &String::from_utf8(b"foo".to_vec()).unwrap() - }; - } - let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap(); - let _ref_str: &str = string_from_utf8_from_macro!(); -} - -fn require_c_str(_: &CStr) {} -fn require_os_str(_: &OsStr) {} -fn require_path(_: &std::path::Path) {} -fn require_str(_: &str) {} -fn require_slice<T>(_: &[T]) {} -fn require_x(_: &X) {} - -fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {} -fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {} -fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {} -fn require_deref_str<T: Deref<Target = str>>(_: T) {} -fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {} - -fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {} -fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {} -fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {} -fn require_impl_deref_str(_: impl Deref<Target = str>) {} -fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {} - -fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {} -fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {} - -fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {} -fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {} -fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {} -fn require_as_ref_str<T: AsRef<str>>(_: T) {} -fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {} - -fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {} -fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {} -fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {} -fn require_impl_as_ref_str(_: impl AsRef<str>) {} -fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {} - -fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {} -fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {} - -// `check_files` is based on: -// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 -fn check_files(file_types: &[FileType]) -> bool { - for t in file_types { - let path = match get_file_path(t) { - Ok(p) => p, - Err(_) => { - return false; - }, - }; - if !path.is_file() { - return false; - } - } - true -} - -fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> { - Ok(std::path::PathBuf::new()) -} - -fn require_string(_: &String) {} - -#[clippy::msrv = "1.35"] -fn _msrv_1_35() { - // `copied` was stabilized in 1.36, so clippy should use `cloned`. - let _ = &["x"][..].iter().cloned(); -} - -#[clippy::msrv = "1.36"] -fn _msrv_1_36() { - let _ = &["x"][..].iter().copied(); -} - -// https://github.com/rust-lang/rust-clippy/issues/8507 -mod issue_8507 { - #![allow(dead_code)] - - struct Opaque<P>(P); - - pub trait Abstracted {} - - impl<P> Abstracted for Opaque<P> {} - - fn build<P>(p: P) -> Opaque<P> - where - P: AsRef<str>, - { - Opaque(p) - } - - // Should not lint. - fn test_str(s: &str) -> Box<dyn Abstracted> { - Box::new(build(s.to_string())) - } - - // Should not lint. - fn test_x(x: super::X) -> Box<dyn Abstracted> { - Box::new(build(x)) - } - - #[derive(Clone, Copy)] - struct Y(&'static str); - - impl AsRef<str> for Y { - fn as_ref(&self) -> &str { - self.0 - } - } - - #[allow(clippy::to_string_trait_impl)] - impl ToString for Y { - fn to_string(&self) -> String { - self.0.to_string() - } - } - - // Should lint because Y is copy. - fn test_y(y: Y) -> Box<dyn Abstracted> { - Box::new(build(y)) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/8759 -mod issue_8759 { - #![allow(dead_code)] - - #[derive(Default)] - struct View {} - - impl std::borrow::ToOwned for View { - type Owned = View; - fn to_owned(&self) -> Self::Owned { - View {} - } - } - - #[derive(Default)] - struct RenderWindow { - default_view: View, - } - - impl RenderWindow { - fn default_view(&self) -> &View { - &self.default_view - } - fn set_view(&mut self, _view: &View) {} - } - - fn main() { - let mut rw = RenderWindow::default(); - rw.set_view(&rw.default_view().to_owned()); - } -} - -mod issue_8759_variant { - #![allow(dead_code)] - - #[derive(Clone, Default)] - struct View {} - - #[derive(Default)] - struct RenderWindow { - default_view: View, - } - - impl RenderWindow { - fn default_view(&self) -> &View { - &self.default_view - } - fn set_view(&mut self, _view: &View) {} - } - - fn main() { - let mut rw = RenderWindow::default(); - rw.set_view(&rw.default_view().to_owned()); - } -} - -mod issue_9317 { - #![allow(dead_code)] - - struct Bytes {} - - #[allow(clippy::to_string_trait_impl)] - impl ToString for Bytes { - fn to_string(&self) -> String { - "123".to_string() - } - } - - impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - &[1, 2, 3] - } - } - - fn consume<C: AsRef<[u8]>>(c: C) { - let _ = c; - } - - pub fn main() { - let b = Bytes {}; - // Should not lint. - consume(b.to_string()); - } -} - -mod issue_9351 { - #![allow(dead_code)] - - use std::ops::Deref; - use std::path::{Path, PathBuf}; - - fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T { - x - } - - fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {} - - fn id<T: AsRef<str>>(x: T) -> T { - x - } - - fn predicates_are_satisfied(_x: impl std::fmt::Write) {} - - // Should lint - fn single_return() -> impl AsRef<str> { - id("abc") - } - - // Should not lint - fn multiple_returns(b: bool) -> impl AsRef<str> { - if b { - return String::new(); - } - - id("abc".to_string()) - } - - struct S1(String); - - // Should not lint - fn fields1() -> S1 { - S1(id("abc".to_string())) - } - - struct S2 { - s: String, - } - - // Should not lint - fn fields2() { - let mut s = S2 { s: "abc".into() }; - s.s = id("abc".to_string()); - } - - pub fn main() { - let path = std::path::Path::new("x"); - let path_buf = path.to_owned(); - - // Should not lint. - let _x: PathBuf = require_deref_path(path.to_owned()); - generic_arg_used_elsewhere(path.to_owned(), path_buf); - predicates_are_satisfied(id("abc".to_string())); - } -} - -mod issue_9504 { - #![allow(dead_code)] - - async fn foo<S: AsRef<str>>(_: S) {} - async fn bar() { - foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; - } -} - -mod issue_9771a { - #![allow(dead_code)] - - use std::marker::PhantomData; - - pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>); - - impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> { - pub fn new(key: K) -> Key<K, V> { - Key(key, PhantomData) - } - } - - pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> { - Key::new([b"pkh-", pkh].concat().to_vec()) - } -} - -mod issue_9771b { - #![allow(dead_code)] - - pub struct Key<K: AsRef<[u8]>>(K); - - pub fn from(c: &[u8]) -> Key<Vec<u8>> { - let v = [c].concat(); - Key(v.to_vec()) - } -} - -// This is a watered down version of the code in: https://github.com/oxigraph/rio -// The ICE is triggered by the call to `to_owned` on this line: -// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 -mod issue_10021 { - #![allow(unused)] - - pub struct Iri<T>(T); - - impl<T: AsRef<str>> Iri<T> { - pub fn parse(iri: T) -> Result<Self, ()> { - unimplemented!() - } - } - - pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { - let base_iri = Iri::parse(url.to_owned())?; - Ok(()) - } -} - -mod issue_10033 { - #![allow(dead_code)] - use std::fmt::Display; - use std::ops::Deref; - - fn _main() { - let f = Foo; - - // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does - // deref to `str`) - foo(&f.to_string()); - } - - fn foo(s: &str) { - println!("{}", s); - } - - struct Foo; - - impl Deref for Foo { - type Target = str; - - fn deref(&self) -> &Self::Target { - "str" - } - } - - impl Display for Foo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Foo") - } - } -} - -mod issue_11952 { - use core::future::{Future, IntoFuture}; - - fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> { - async move { - let _y = y; - Ok(()) - } - } - - fn bar() { - IntoFuture::into_future(foo([], &0)); - } -} - -fn borrow_checks() { - use std::borrow::Borrow; - use std::collections::HashSet; - - fn inner(a: &[&str]) { - let mut s = HashSet::from([vec!["a"]]); - s.remove(a); //~ ERROR: unnecessary use of `to_vec` - } - - let mut s = HashSet::from(["a".to_string()]); - s.remove("b"); //~ ERROR: unnecessary use of `to_owned` - s.remove("b"); //~ ERROR: unnecessary use of `to_string` - // Should not warn. - s.remove("b"); - - let mut s = HashSet::from([vec!["a"]]); - s.remove(["b"].as_slice()); //~ ERROR: unnecessary use of `to_vec` - s.remove((&["b"]).as_slice()); //~ ERROR: unnecessary use of `to_vec` - - // Should not warn. - s.remove(&["b"].to_vec().clone()); - s.remove(["a"].as_slice()); - - trait SetExt { - fn foo<Q: Borrow<str>>(&self, _: &String); - } - - impl<K> SetExt for HashSet<K> { - fn foo<Q: Borrow<str>>(&self, _: &String) {} - } - - // Should not lint! - HashSet::<i32>::new().foo::<&str>(&"".to_owned()); - HashSet::<String>::new().get(&1.to_string()); -} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 10a9727a9a7..da0c761f795 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -7,6 +7,8 @@ )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] +//@no-rustfix: need to change the suggestion to a multipart suggestion + use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 511b4ae119f..7ab1f667d9b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:155:64 + --> tests/ui/unnecessary_to_owned.rs:157:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:155:20 + --> tests/ui/unnecessary_to_owned.rs:157:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:156:40 + --> tests/ui/unnecessary_to_owned.rs:158:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:156:21 + --> tests/ui/unnecessary_to_owned.rs:158:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:157:48 + --> tests/ui/unnecessary_to_owned.rs:159:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:157:19 + --> tests/ui/unnecessary_to_owned.rs:159:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:158:35 + --> tests/ui/unnecessary_to_owned.rs:160:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:158:18 + --> tests/ui/unnecessary_to_owned.rs:160:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:159:39 + --> tests/ui/unnecessary_to_owned.rs:161:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:159:20 + --> tests/ui/unnecessary_to_owned.rs:161:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:64:36 + --> tests/ui/unnecessary_to_owned.rs:66:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:65:19 + --> tests/ui/unnecessary_to_owned.rs:67:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:67:20 + --> tests/ui/unnecessary_to_owned.rs:69:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:68:38 + --> tests/ui/unnecessary_to_owned.rs:70:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:69:20 + --> tests/ui/unnecessary_to_owned.rs:71:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:71:18 + --> tests/ui/unnecessary_to_owned.rs:73:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:72:34 + --> tests/ui/unnecessary_to_owned.rs:74:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:73:18 + --> tests/ui/unnecessary_to_owned.rs:75:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:75:17 + --> tests/ui/unnecessary_to_owned.rs:77:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:76:30 + --> tests/ui/unnecessary_to_owned.rs:78:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:77:17 + --> tests/ui/unnecessary_to_owned.rs:79:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:78:17 + --> tests/ui/unnecessary_to_owned.rs:80:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:80:19 + --> tests/ui/unnecessary_to_owned.rs:82:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:81:36 + --> tests/ui/unnecessary_to_owned.rs:83:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:82:19 + --> tests/ui/unnecessary_to_owned.rs:84:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:83:19 + --> tests/ui/unnecessary_to_owned.rs:85:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:84:19 + --> tests/ui/unnecessary_to_owned.rs:86:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:87:42 + --> tests/ui/unnecessary_to_owned.rs:89:42 | LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:90:25 + --> tests/ui/unnecessary_to_owned.rs:92:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:91:26 + --> tests/ui/unnecessary_to_owned.rs:93:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:92:24 + --> tests/ui/unnecessary_to_owned.rs:94:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:93:23 + --> tests/ui/unnecessary_to_owned.rs:95:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:94:25 + --> tests/ui/unnecessary_to_owned.rs:96:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:96:30 + --> tests/ui/unnecessary_to_owned.rs:98:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:97:31 + --> tests/ui/unnecessary_to_owned.rs:99:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:98:29 + --> tests/ui/unnecessary_to_owned.rs:100:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:28 + --> tests/ui/unnecessary_to_owned.rs:101:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:100:30 + --> tests/ui/unnecessary_to_owned.rs:102:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:102:29 + --> tests/ui/unnecessary_to_owned.rs:104:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:102:43 + --> tests/ui/unnecessary_to_owned.rs:104:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:103:29 + --> tests/ui/unnecessary_to_owned.rs:105:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:103:47 + --> tests/ui/unnecessary_to_owned.rs:105:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:105:26 + --> tests/ui/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:106:27 + --> tests/ui/unnecessary_to_owned.rs:108:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:107:25 + --> tests/ui/unnecessary_to_owned.rs:109:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:108:24 + --> tests/ui/unnecessary_to_owned.rs:110:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:24 + --> tests/ui/unnecessary_to_owned.rs:111:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:110:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:26 + --> tests/ui/unnecessary_to_owned.rs:113:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:112:26 + --> tests/ui/unnecessary_to_owned.rs:114:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:114:31 + --> tests/ui/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:115:32 + --> tests/ui/unnecessary_to_owned.rs:117:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:116:30 + --> tests/ui/unnecessary_to_owned.rs:118:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:29 + --> tests/ui/unnecessary_to_owned.rs:119:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:118:29 + --> tests/ui/unnecessary_to_owned.rs:120:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:119:31 + --> tests/ui/unnecessary_to_owned.rs:121:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:31 + --> tests/ui/unnecessary_to_owned.rs:122:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:121:31 + --> tests/ui/unnecessary_to_owned.rs:123:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:30 + --> tests/ui/unnecessary_to_owned.rs:125:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:44 + --> tests/ui/unnecessary_to_owned.rs:125:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:30 + --> tests/ui/unnecessary_to_owned.rs:126:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:44 + --> tests/ui/unnecessary_to_owned.rs:126:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:30 + --> tests/ui/unnecessary_to_owned.rs:127:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:44 + --> tests/ui/unnecessary_to_owned.rs:127:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:30 + --> tests/ui/unnecessary_to_owned.rs:128:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:48 + --> tests/ui/unnecessary_to_owned.rs:128:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:30 + --> tests/ui/unnecessary_to_owned.rs:129:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:52 + --> tests/ui/unnecessary_to_owned.rs:129:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:130:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:48 + --> tests/ui/unnecessary_to_owned.rs:130:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:130:20 + --> tests/ui/unnecessary_to_owned.rs:132:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:132:13 + --> tests/ui/unnecessary_to_owned.rs:134:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:133:13 + --> tests/ui/unnecessary_to_owned.rs:135:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:134:13 + --> tests/ui/unnecessary_to_owned.rs:136:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:135:13 + --> tests/ui/unnecessary_to_owned.rs:137:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:137:13 + --> tests/ui/unnecessary_to_owned.rs:139:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:138:13 + --> tests/ui/unnecessary_to_owned.rs:140:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:139:13 + --> tests/ui/unnecessary_to_owned.rs:141:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:13 + --> tests/ui/unnecessary_to_owned.rs:142:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:162:26 + --> tests/ui/unnecessary_to_owned.rs:164:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:163:26 + --> tests/ui/unnecessary_to_owned.rs:165:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +502,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:164:26 + --> tests/ui/unnecessary_to_owned.rs:166:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +514,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:221:14 + --> tests/ui/unnecessary_to_owned.rs:223:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -530,61 +530,61 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:244:14 + --> tests/ui/unnecessary_to_owned.rs:246:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:249:14 + --> tests/ui/unnecessary_to_owned.rs:251:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:297:24 + --> tests/ui/unnecessary_to_owned.rs:299:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:406:12 + --> tests/ui/unnecessary_to_owned.rs:408:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:549:37 + --> tests/ui/unnecessary_to_owned.rs:551:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:559:18 + --> tests/ui/unnecessary_to_owned.rs:561:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:563:14 + --> tests/ui/unnecessary_to_owned.rs:565:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:564:14 + --> tests/ui/unnecessary_to_owned.rs:566:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:569:14 + --> tests/ui/unnecessary_to_owned.rs:571:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:570:14 + --> tests/ui/unnecessary_to_owned.rs:572:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed index 6f132521926..989e8ae70e5 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed @@ -16,13 +16,11 @@ fn main() { // on arrays f(); let a: [i32; 0] = []; - f(); let a: [i32; 0] = []; let mut b; f(); b = [] as [i32; 0]; - f(); b = [] as [i32; 0]; // on vecs - // vecs dont support infering value of consts + // vecs dont support inferring value of consts f(); let c: std::vec::Vec<i32> = vec![]; let d; f(); d = vec![] as std::vec::Vec<i32>; @@ -39,9 +37,11 @@ fn main() { // when singled out/not part of assignment/local { f(); vec![] as std::vec::Vec<i32> }; { f(); [] as [i32; 0] }; - { f(); [] as [i32; 0] }; // should not trigger + let a = [f(); N]; + b = [f(); N]; + [f(); N]; // on arrays with > 0 repeat let a = [f(); 1]; @@ -58,3 +58,15 @@ fn main() { // as function param drop(vec![f(); 1]); } + +macro_rules! LEN { + () => { + 0 + }; +} + +fn issue_13110() { + let _data = [f(); LEN!()]; + const LENGTH: usize = LEN!(); + let _data = [f(); LENGTH]; +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs index 9d9c367375a..68511f41a95 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs @@ -16,13 +16,11 @@ fn main() { // on arrays let a = [f(); 0]; - let a = [f(); N]; let mut b; b = [f(); 0]; - b = [f(); N]; // on vecs - // vecs dont support infering value of consts + // vecs dont support inferring value of consts let c = vec![f(); 0]; let d; d = vec![f(); 0]; @@ -39,9 +37,11 @@ fn main() { // when singled out/not part of assignment/local vec![f(); 0]; [f(); 0]; - [f(); N]; // should not trigger + let a = [f(); N]; + b = [f(); N]; + [f(); N]; // on arrays with > 0 repeat let a = [f(); 1]; @@ -58,3 +58,15 @@ fn main() { // as function param drop(vec![f(); 1]); } + +macro_rules! LEN { + () => { + 0 + }; +} + +fn issue_13110() { + let _data = [f(); LEN!()]; + const LENGTH: usize = LEN!(); + let _data = [f(); LENGTH]; +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr index afdc6054253..d578e22b971 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr @@ -8,70 +8,52 @@ LL | let a = [f(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:19:5 - | -LL | let a = [f(); N]; - | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` - -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:21:5 + --> tests/ui/zero_repeat_side_effects.rs:20:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:22:5 - | -LL | b = [f(); N]; - | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` - -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:26:5 + --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec<i32> = vec![];` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:28:5 + --> tests/ui/zero_repeat_side_effects.rs:26:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec<i32>` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:31:5 + --> tests/ui/zero_repeat_side_effects.rs:29:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:34:5 + --> tests/ui/zero_repeat_side_effects.rs:32:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:37:10 + --> tests/ui/zero_repeat_side_effects.rs:35:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec<i32> }` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:40:5 + --> tests/ui/zero_repeat_side_effects.rs:38:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec<i32> }` error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:41:5 + --> tests/ui/zero_repeat_side_effects.rs:39:5 | LL | [f(); 0]; | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:42:5 - | -LL | [f(); N]; - | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` - -error: aborting due to 12 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 0f0e62670ff..dcf00e4e384 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -31,10 +31,10 @@ users_on_vacation = [ "*" = [ "@Manishearth", "@llogiq", + "@xFrednet", "@Alexendoo", "@dswij", "@Jarcho", - "@blyxyas", "@y21", "@Centri3", ] diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html index 8de36fc4005..0c0f28e4fbd 100644 --- a/src/tools/clippy/util/gh-pages/index.html +++ b/src/tools/clippy/util/gh-pages/index.html @@ -57,20 +57,17 @@ Otherwise, have a great day =^.^= background-color: var(--theme-hover); } - div.panel div.panel-body button.dropdown-toggle { + div.panel div.panel-body button { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); } - div.panel div.panel-body button.dropdown-toggle:hover { + div.panel div.panel-body button:hover { box-shadow: 0 0 3px var(--searchbar-shadow-color); } - div.panel div.panel-body .open button.dropdown-toggle { - background: var(--searchbar-bg); - color: var(--searchbar-fg); - border-color: var(--theme-popup-border); + div.panel div.panel-body button.open { filter: brightness(90%); } @@ -96,7 +93,6 @@ Otherwise, have a great day =^.^= @media (min-width: 992px) { .search-control { margin-top: 0; - float: right; } } @@ -361,6 +357,24 @@ Otherwise, have a great day =^.^= opacity: 30%; } + .expansion-group { + margin-top: 15px; + padding: 0px 8px; + display: flex; + flex-wrap: nowrap; + } + + @media (min-width: 992px) { + .expansion-group { + margin-top: 0; + padding: 0px 15px; + } + } + + .expansion-control { + width: 50%; + } + :not(pre) > code { color: var(--inline-code-color); background-color: var(--inline-code-bg); @@ -405,7 +419,7 @@ Otherwise, have a great day =^.^= <div class="panel panel-default" ng-show="data"> <div class="panel-body row"> - <div id="upper-filters" class="col-12 col-md-6"> + <div id="upper-filters" class="col-12 col-md-5"> <div class="btn-group" filter-dropdown> <button type="button" class="btn btn-default dropdown-toggle"> Lint levels <span class="badge">{{selectedValuesCount(levels)}}</span> <span class="caret"></span> @@ -524,7 +538,7 @@ Otherwise, have a great day =^.^= </ul> </div> </div> - <div class="col-12 col-md-6 search-control"> + <div class="col-12 col-md-5 search-control"> <div class="input-group"> <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> <input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input" @@ -537,6 +551,14 @@ Otherwise, have a great day =^.^= </span> </div> </div> + <div class="col-12 col-md-2 btn-group expansion-group"> + <button title="Collapse All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, false)"> + <span class="glyphicon glyphicon-collapse-up"></span> + </button> + <button title="Expand All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, true)"> + <span class="glyphicon glyphicon-collapse-down"></span> + </button> + </div> </div> </div> <!-- The order of the filters should be from most likely to remove a lint to least likely to improve performance. --> diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 921bb0376f6..661f80a6d34 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -469,6 +469,12 @@ $location.path(lint.id); }; + $scope.toggleExpansion = function(lints, isExpanded) { + lints.forEach(lint => { + $scope.open[lint.id] = isExpanded; + }); + } + $scope.copyToClipboard = function (lint) { const clipboard = document.getElementById("clipboard-" + lint.id); if (clipboard) { diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 6735e9faa7a..c356f4266f0 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -201,6 +201,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-wasm32-wasip1", "only-watchos", "only-windows", + "only-windows-gnu", "only-x86", "only-x86_64", "only-x86_64-fortanix-unknown-sgx", diff --git a/src/tools/run-make-support/src/external_deps/cc.rs b/src/tools/run-make-support/src/external_deps/cc.rs index 840bfa0d2b4..19a89705acc 100644 --- a/src/tools/run-make-support/src/external_deps/cc.rs +++ b/src/tools/run-make-support/src/external_deps/cc.rs @@ -15,6 +15,13 @@ pub fn cc() -> Cc { Cc::new() } +/// Construct a new platform-specific CXX compiler invocation. +/// CXX_DEFAULT_FLAGS is passed from compiletest. +#[track_caller] +pub fn cxx() -> Cc { + Cc::new_cxx() +} + /// A platform-specific C compiler invocation builder. The specific C compiler used is /// passed down from compiletest. #[derive(Debug)] @@ -44,6 +51,22 @@ impl Cc { Self { cmd } } + /// Construct a new platform-specific CXX compiler invocation. + /// CXX_DEFAULT_FLAGS is passed from compiletest. + #[track_caller] + pub fn new_cxx() -> Self { + let compiler = env_var("CXX"); + + let mut cmd = Command::new(compiler); + + let default_cflags = env_var("CXX_DEFAULT_FLAGS"); + for flag in default_cflags.split(char::is_whitespace) { + cmd.arg(flag); + } + + Self { cmd } + } + /// Specify path of the input file. pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { self.cmd.arg(path.as_ref()); diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index a4bb9056346..085120764b4 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -44,7 +44,7 @@ pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rust // These rely on external dependencies. pub use c_build::{build_native_dynamic_lib, build_native_static_lib}; -pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc}; +pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc}; pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 36f7f68ef7b..c3993e41a50 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,8 +1,6 @@ run-make/branch-protection-check-IBT/Makefile -run-make/c-unwind-abi-catch-lib-panic/Makefile run-make/cat-and-grep-sanity-check/Makefile run-make/cdylib-dylib-linkage/Makefile -run-make/compiler-rt-works-on-mingw/Makefile run-make/cross-lang-lto-clang/Makefile run-make/cross-lang-lto-pgo-smoketest/Makefile run-make/cross-lang-lto-upstream-rlibs/Makefile @@ -10,18 +8,12 @@ run-make/cross-lang-lto/Makefile run-make/dep-info-doesnt-run-much/Makefile run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile -run-make/dump-ice-to-disk/Makefile run-make/emit-to-stdout/Makefile -run-make/export-executable-symbols/Makefile -run-make/extern-flag-disambiguates/Makefile run-make/extern-fn-reachable/Makefile run-make/fmt-write-bloat/Makefile run-make/foreign-double-unwind/Makefile run-make/foreign-exceptions/Makefile -run-make/foreign-rust-exceptions/Makefile run-make/incr-add-rust-src-component/Makefile -run-make/incr-foreign-head-span/Makefile -run-make/interdependent-c-libraries/Makefile run-make/issue-35164/Makefile run-make/issue-36710/Makefile run-make/issue-47551/Makefile @@ -42,7 +34,6 @@ run-make/native-link-modifier-bundle/Makefile run-make/native-link-modifier-whole-archive/Makefile run-make/no-alloc-shim/Makefile run-make/no-builtins-attribute/Makefile -run-make/panic-abort-eh_frame/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile run-make/pgo-gen-lto/Makefile run-make/pgo-indirect-call-promotion/Makefile diff --git a/tests/assembly/simd-intrinsic-mask-load.rs b/tests/assembly/simd-intrinsic-mask-load.rs index d537c143d36..b650e1cee30 100644 --- a/tests/assembly/simd-intrinsic-mask-load.rs +++ b/tests/assembly/simd-intrinsic-mask-load.rs @@ -18,6 +18,7 @@ pub trait Sized {} #[lang = "copy"] trait Copy {} +impl<T: ?Sized> Copy for *const T {} #[repr(simd)] pub struct i8x16([i8; 16]); diff --git a/tests/assembly/simd-intrinsic-mask-store.rs b/tests/assembly/simd-intrinsic-mask-store.rs index 5d4c00c3823..95a3b28b967 100644 --- a/tests/assembly/simd-intrinsic-mask-store.rs +++ b/tests/assembly/simd-intrinsic-mask-store.rs @@ -18,6 +18,7 @@ pub trait Sized {} #[lang = "copy"] trait Copy {} +impl<T: ?Sized> Copy for *mut T {} #[repr(simd)] pub struct i8x16([i8; 16]); diff --git a/tests/codegen/avr/avr-func-addrspace.rs b/tests/codegen/avr/avr-func-addrspace.rs index 70834707564..7f9a7e6e811 100644 --- a/tests/codegen/avr/avr-func-addrspace.rs +++ b/tests/codegen/avr/avr-func-addrspace.rs @@ -17,6 +17,7 @@ pub trait Sized {} #[lang = "copy"] pub trait Copy {} +impl<T: ?Sized> Copy for *const T {} #[lang = "receiver"] pub trait Receiver {} #[lang = "tuple_trait"] diff --git a/tests/codegen/clone-shims.rs b/tests/codegen/clone-shims.rs new file mode 100644 index 00000000000..06c959f9ee7 --- /dev/null +++ b/tests/codegen/clone-shims.rs @@ -0,0 +1,15 @@ +// Clone shims for aggregates are generated by just calling the Clone shims for all their members. +// Those calls generate a lot of unnecessary IR if the members are Copy. This test ensures that we +// optimize away those inner calls without needing to inline them. + +//@ compile-flags: -Cno-prepopulate-passes -Csymbol-mangling-version=v0 -Zinline-mir=no +#![crate_type = "lib"] + +pub type Test = (i32, i32, *const i32); +pub static TEST: fn(&Test) -> Test = <Test as core::clone::Clone>::clone; + +// CHECK-NOT: call <i32 as core::clone::Clone>::clone +// CHECK-NOT: call <*const i32 as core::clone::Clone>::clone +// CHECK: ; <(i32, i32, *const i32) as core::clone::Clone>::clone +// CHECK-NOT: call <i32 as core::clone::Clone>::clone +// CHECK-NOT: call <*const i32 as core::clone::Clone>::clone diff --git a/tests/codegen/emcripten-catch-unwind.rs b/tests/codegen/emcripten-catch-unwind.rs index 6cda8c6799f..35444db9558 100644 --- a/tests/codegen/emcripten-catch-unwind.rs +++ b/tests/codegen/emcripten-catch-unwind.rs @@ -16,6 +16,8 @@ trait Freeze {} #[lang = "copy"] trait Copy {} +impl<T> Copy for *mut T {} + #[rustc_intrinsic] fn size_of<T>() -> usize { loop {} diff --git a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs index ed0af90aaaf..520192b5d59 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs @@ -18,6 +18,7 @@ impl Copy for i64 {} impl Copy for u64 {} impl Copy for f32 {} impl Copy for f64 {} +impl<T> Copy for *mut T {} // CHECK: define void @f_void() #[no_mangle] diff --git a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs index fc4d570dc2e..c1967e55e75 100644 --- a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs @@ -15,6 +15,7 @@ trait Sized {} #[lang = "copy"] trait Copy {} +impl<T: ?Sized> Copy for &T {} #[lang = "receiver"] trait Receiver {} #[lang = "dispatch_from_dyn"] diff --git a/tests/run-make/README.md b/tests/run-make/README.md index a6c1b4b7db7..40359903473 100644 --- a/tests/run-make/README.md +++ b/tests/run-make/README.md @@ -41,3 +41,8 @@ The setup for the `rmake.rs` version is a 3-stage process: [`run_make_support`]: ../../src/tools/run-make-support [extern_prelude]: https://doc.rust-lang.org/reference/names/preludes.html#extern-prelude + +### Formatting + +Note that files under `tests/` are not formatted by `./x fmt`, +use `rustfmt tests/path/to/file.rs` to format a specific file if desired. diff --git a/tests/run-make/c-unwind-abi-catch-lib-panic/Makefile b/tests/run-make/c-unwind-abi-catch-lib-panic/Makefile deleted file mode 100644 index 2bb8d42495d..00000000000 --- a/tests/run-make/c-unwind-abi-catch-lib-panic/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# Exercise unwinding a panic. This catches a panic across an FFI boundary and downcasts it into an integer. The Rust code that panics is in a separate crate. -# See https://github.com/rust-lang/rust/commit/baf227ea0c1e07fc54395a51e4b3881d701180cb - -# ignore-cross-compile -# needs-unwind -include ../tools.mk - -all: archive - # Compile `main.rs`, which will link into our library, and run it. - $(RUSTC) main.rs - $(call RUN,main) - -ifdef IS_MSVC -archive: add.o panic.o - # Now, create an archive using these two objects. - $(AR) crus $(TMPDIR)/add.lib $(TMPDIR)/add.o $(TMPDIR)/panic.o -else -archive: add.o panic.o - # Now, create an archive using these two objects. - $(AR) crus $(TMPDIR)/libadd.a $(TMPDIR)/add.o $(TMPDIR)/panic.o -endif - -# Compile `panic.rs` into an object file. -# -# Note that we invoke `rustc` directly, so we may emit an object rather -# than an archive. We'll do that later. -panic.o: - $(BARE_RUSTC) $(RUSTFLAGS) \ - --out-dir $(TMPDIR) \ - --emit=obj panic.rs - -# Compile `add.c` into an object file. -add.o: - $(call COMPILE_OBJ,$(TMPDIR)/add.o,add.c) - diff --git a/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs b/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs new file mode 100644 index 00000000000..62e1748b6fb --- /dev/null +++ b/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs @@ -0,0 +1,36 @@ +// Exercise unwinding a panic. This catches a panic across an FFI (foreign function interface) +// boundary and downcasts it into an integer. +// The Rust code that panics is in a separate crate. +// See https://github.com/rust-lang/rust/commit/baf227ea0c1e07fc54395a51e4b3881d701180cb + +//@ ignore-cross-compile +// Reason: the compiled binary is executed +//@ needs-unwind +// Reason: this test exercises unwinding a panic + +use run_make_support::{cc, is_msvc, llvm_ar, run, rustc, static_lib_name}; + +fn main() { + // Compile `add.c` into an object file. + if is_msvc() { + cc().arg("-c").out_exe("add").input("add.c").run(); + } else { + cc().arg("-v").arg("-c").out_exe("add.o").input("add.c").run(); + }; + + // Compile `panic.rs` into an object file. + // Note that we invoke `rustc` directly, so we may emit an object rather + // than an archive. We'll do that later. + rustc().emit("obj").input("panic.rs").run(); + + // Now, create an archive using these two objects. + if is_msvc() { + llvm_ar().obj_to_ar().args(&[&static_lib_name("add"), "add.obj", "panic.o"]).run(); + } else { + llvm_ar().obj_to_ar().args(&[&static_lib_name("add"), "add.o", "panic.o"]).run(); + }; + + // Compile `main.rs`, which will link into our library, and run it. + rustc().input("main.rs").run(); + run("main"); +} diff --git a/tests/run-make/compiler-rt-works-on-mingw/Makefile b/tests/run-make/compiler-rt-works-on-mingw/Makefile deleted file mode 100644 index 74917570a01..00000000000 --- a/tests/run-make/compiler-rt-works-on-mingw/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# only-windows-gnu - -all: - $(CXX) foo.cpp -c -o $(TMPDIR)/foo.o - $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o - $(RUSTC) foo.rs -lfoo -lstdc++ - $(call RUN,foo) diff --git a/tests/run-make/compiler-rt-works-on-mingw/rmake.rs b/tests/run-make/compiler-rt-works-on-mingw/rmake.rs new file mode 100644 index 00000000000..f1b41f96312 --- /dev/null +++ b/tests/run-make/compiler-rt-works-on-mingw/rmake.rs @@ -0,0 +1,15 @@ +// `compiler-rt` ("runtime") is a suite of LLVM features compatible with rustc. +// After building it was enabled on Windows-gnu in #29874, this test is a basic smoke test to +// check if building and linking to it can work at all. +// See https://github.com/rust-lang/rust/pull/29478 + +//@ only-windows-gnu + +use run_make_support::{cxx, is_msvc, llvm_ar, run, rustc, static_lib_name}; + +fn main() { + cxx().input("foo.cpp").arg("-c").out_exe("foo.o").run(); + llvm_ar().obj_to_ar().output_input(static_lib_name("foo"), "foo.o").run(); + rustc().input("foo.rs").arg("-lfoo").arg("-lstdc++").run(); + run("foo"); +} diff --git a/tests/run-make/dump-ice-to-disk/Makefile b/tests/run-make/dump-ice-to-disk/Makefile deleted file mode 100644 index 23006fc09e2..00000000000 --- a/tests/run-make/dump-ice-to-disk/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -include ../tools.mk - -# ignore-windows - -export RUSTC := $(RUSTC_ORIGINAL) -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -export TMPDIR := $(TMPDIR) - -all: - bash check.sh diff --git a/tests/run-make/dump-ice-to-disk/check.sh b/tests/run-make/dump-ice-to-disk/check.sh deleted file mode 100644 index ff6e4be35af..00000000000 --- a/tests/run-make/dump-ice-to-disk/check.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh - -# Default nightly behavior (write ICE to current directory) -# FIXME(estebank): these are failing on CI, but passing locally. -# $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-default.log 2>&1 -# default=$(cat ./rustc-ice-*.txt | wc -l) -# rm ./rustc-ice-*.txt - -# Explicit directory set -export RUSTC_ICE=$TMPDIR -$RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-default-set.log 2>&1 -default_set=$(cat $TMPDIR/rustc-ice-*.txt | wc -l) -content=$(cat $TMPDIR/rustc-ice-*.txt) -# Ensure that the ICE dump path doesn't contain `:` because they cause problems on Windows -windows_safe=$(echo rustc-ice-*.txt | grep ':') -if [ ! -z "$windows_safe" ]; then - exit 1 -fi - -rm $TMPDIR/rustc-ice-*.txt -RUST_BACKTRACE=short $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-short.log 2>&1 -short=$(cat $TMPDIR/rustc-ice-*.txt | wc -l) -rm $TMPDIR/rustc-ice-*.txt -RUST_BACKTRACE=full $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-full.log 2>&1 -full=$(cat $TMPDIR/rustc-ice-*.txt | wc -l) -rm $TMPDIR/rustc-ice-*.txt - -# Explicitly disabling ICE dump -export RUSTC_ICE=0 -$RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-disabled.log 2>&1 -should_be_empty_tmp=$(ls -l $TMPDIR/rustc-ice-*.txt 2>/dev/null | wc -l) -should_be_empty_dot=$(ls -l ./rustc-ice-*.txt 2>/dev/null | wc -l) - -echo "#### ICE Dump content:" -echo $content -echo "#### default length:" -echo $default -echo "#### short length:" -echo $short -echo "#### default_set length:" -echo $default_set -echo "#### full length:" -echo $full -echo "#### should_be_empty_dot length:" -echo $should_be_empty_dot -echo "#### should_be_empty_tmp length:" -echo $should_be_empty_tmp - -## Verify that a the ICE dump file is created in the appropriate directories, that -## their lengths are the same regardless of other backtrace configuration options, -## that the file is not created when asked to (RUSTC_ICE=0) and that the file -## contains at least part of the expected content. -if [ $short -eq $default_set ] && - #[ $default -eq $short ] && - [ $default_set -eq $full ] && - [[ $content == *"thread 'rustc' panicked at "* ]] && - [[ $content == *"stack backtrace:"* ]] && - #[ $default -gt 0 ] && - [ $should_be_empty_dot -eq 0 ] && - [ $should_be_empty_tmp -eq 0 ]; then - exit 0 -else - exit 1 -fi diff --git a/tests/run-make/dump-ice-to-disk/src/lib.rs b/tests/run-make/dump-ice-to-disk/lib.rs index b23b7f830d7..b23b7f830d7 100644 --- a/tests/run-make/dump-ice-to-disk/src/lib.rs +++ b/tests/run-make/dump-ice-to-disk/lib.rs diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs new file mode 100644 index 00000000000..2fb5c825064 --- /dev/null +++ b/tests/run-make/dump-ice-to-disk/rmake.rs @@ -0,0 +1,81 @@ +// This test checks if internal compilation error (ICE) log files work as expected. +// - Get the number of lines from the log files without any configuration options, +// then check that the line count doesn't change if the backtrace gets configured to be short +// or full. +// - Check that disabling ICE logging results in zero files created. +// - Check that the ICE files contain some of the expected strings. +// See https://github.com/rust-lang/rust/pull/108714 + +use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_files}; + +fn main() { + rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let default = get_text_from_ice(".").lines().count(); + clear_ice_files(); + + rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let ice_text = get_text_from_ice(cwd()); + let default_set = ice_text.lines().count(); + let content = ice_text; + let ice_files = shallow_find_files(cwd(), |path| { + has_prefix(path, "rustc-ice") && has_extension(path, "txt") + }); + assert_eq!(ice_files.len(), 1); // There should only be 1 ICE file. + let ice_file_name = + ice_files.first().and_then(|f| f.file_name()).and_then(|n| n.to_str()).unwrap(); + // Ensure that the ICE dump path doesn't contain `:`, because they cause problems on Windows. + assert!(!ice_file_name.contains(":"), "{ice_file_name}"); + + clear_ice_files(); + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "short") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let short = get_text_from_ice(cwd()).lines().count(); + clear_ice_files(); + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "full") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let full = get_text_from_ice(cwd()).lines().count(); + clear_ice_files(); + + // The ICE dump is explicitly disabled. Therefore, this should produce no files. + rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let ice_files = shallow_find_files(cwd(), |path| { + has_prefix(path, "rustc-ice") && has_extension(path, "txt") + }); + assert!(ice_files.is_empty()); // There should be 0 ICE files. + + // The line count should not change. + assert_eq!(short, default_set); + assert_eq!(short, default); + assert_eq!(full, default_set); + assert!(default > 0); + // Some of the expected strings in an ICE file should appear. + assert!(content.contains("thread 'rustc' panicked at")); + assert!(content.contains("stack backtrace:")); +} + +fn clear_ice_files() { + let ice_files = shallow_find_files(cwd(), |path| { + has_prefix(path, "rustc-ice") && has_extension(path, "txt") + }); + for file in ice_files { + rfs::remove_file(file); + } +} + +#[track_caller] +fn get_text_from_ice(dir: impl AsRef<std::path::Path>) -> String { + let ice_files = + shallow_find_files(dir, |path| has_prefix(path, "rustc-ice") && has_extension(path, "txt")); + assert_eq!(ice_files.len(), 1); // There should only be 1 ICE file. + let ice_file = ice_files.get(0).unwrap(); + let output = rfs::read_to_string(ice_file); + output +} diff --git a/tests/run-make/export-executable-symbols/Makefile b/tests/run-make/export-executable-symbols/Makefile deleted file mode 100644 index c4d29aa2bf4..00000000000 --- a/tests/run-make/export-executable-symbols/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -include ../tools.mk - -# ignore-wasm32 -# ignore-wasm64 -# ignore-none no-std is not supported -# only-linux - -all: - $(RUSTC) -Zexport-executable-symbols main.rs --target $(TARGET) --crate-type=bin - nm $(TMPDIR)/main | $(CGREP) exported_symbol - diff --git a/tests/run-make/export-executable-symbols/rmake.rs b/tests/run-make/export-executable-symbols/rmake.rs new file mode 100644 index 00000000000..77f968189b6 --- /dev/null +++ b/tests/run-make/export-executable-symbols/rmake.rs @@ -0,0 +1,25 @@ +// The unstable flag `-Z export-executable-symbols` exports symbols from executables, as if +// they were dynamic libraries. This test is a simple smoke test to check that this feature +// works by using it in compilation, then checking that the output binary contains the exported +// symbol. +// See https://github.com/rust-lang/rust/pull/85673 + +//@ only-unix +// Reason: the export-executable-symbols flag only works on Unix +// due to hardcoded platform-specific implementation +// (See #85673) +//@ ignore-wasm32 +//@ ignore-wasm64 +//@ ignore-none +// Reason: no-std is not supported + +use run_make_support::{bin_name, llvm_readobj, rustc}; + +fn main() { + rustc().arg("-Zexport-executable-symbols").input("main.rs").crate_type("bin").run(); + llvm_readobj() + .symbols() + .input(bin_name("main")) + .run() + .assert_stdout_contains("exported_symbol"); +} diff --git a/tests/run-make/extern-flag-disambiguates/Makefile b/tests/run-make/extern-flag-disambiguates/Makefile deleted file mode 100644 index e54a537ecd0..00000000000 --- a/tests/run-make/extern-flag-disambiguates/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Attempt to build this dependency tree: -# -# A.1 A.2 -# |\ | -# | \ | -# B \ C -# \ | / -# \|/ -# D -# -# Note that A.1 and A.2 are crates with the same name. - -all: - $(RUSTC) -C metadata=1 -C extra-filename=-1 a.rs - $(RUSTC) -C metadata=2 -C extra-filename=-2 a.rs - $(RUSTC) b.rs --extern a=$(TMPDIR)/liba-1.rlib - $(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib - @echo before - $(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib - $(call RUN,d) - @echo after - $(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib - $(call RUN,d) diff --git a/tests/run-make/extern-flag-disambiguates/rmake.rs b/tests/run-make/extern-flag-disambiguates/rmake.rs new file mode 100644 index 00000000000..2d7d7f69f66 --- /dev/null +++ b/tests/run-make/extern-flag-disambiguates/rmake.rs @@ -0,0 +1,30 @@ +//@ ignore-cross-compile + +use run_make_support::{cwd, run, rustc}; + +// Attempt to build this dependency tree: +// +// A.1 A.2 +// |\ | +// | \ | +// B \ C +// \ | / +// \|/ +// D +// +// Note that A.1 and A.2 are crates with the same name. + +// original Makefile at https://github.com/rust-lang/rust/issues/14469 + +fn main() { + rustc().metadata("1").extra_filename("-1").input("a.rs").run(); + rustc().metadata("2").extra_filename("-2").input("a.rs").run(); + rustc().input("b.rs").extern_("a", "liba-1.rlib").run(); + rustc().input("c.rs").extern_("a", "liba-2.rlib").run(); + println!("before"); + rustc().cfg("before").input("d.rs").extern_("a", "liba-1.rlib").run(); + run("d"); + println!("after"); + rustc().cfg("after").input("d.rs").extern_("a", "liba-1.rlib").run(); + run("d"); +} diff --git a/tests/run-make/foreign-rust-exceptions/Makefile b/tests/run-make/foreign-rust-exceptions/Makefile deleted file mode 100644 index 59cee284200..00000000000 --- a/tests/run-make/foreign-rust-exceptions/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# ignore-cross-compile -# ignore-i686-pc-windows-gnu -# needs-unwind - -# This test doesn't work on 32-bit MinGW as cdylib has its own copy of unwinder -# so cross-DLL unwinding does not work. - -include ../tools.mk - -all: - $(RUSTC) bar.rs --crate-type=cdylib - $(RUSTC) foo.rs - $(call RUN,foo) 2>&1 | $(CGREP) "Rust cannot catch foreign exceptions" diff --git a/tests/run-make/foreign-rust-exceptions/rmake.rs b/tests/run-make/foreign-rust-exceptions/rmake.rs new file mode 100644 index 00000000000..9c917078aaa --- /dev/null +++ b/tests/run-make/foreign-rust-exceptions/rmake.rs @@ -0,0 +1,23 @@ +// Rust exceptions can be foreign (from C code, in this test) or local. Foreign +// exceptions should not be caught, as that can cause undefined behaviour. Instead +// of catching them, #102721 made it so that the binary panics in execution with a helpful message. +// This test checks that the correct message appears and that execution fails when trying to catch +// a foreign exception. +// See https://github.com/rust-lang/rust/issues/102715 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed +//@ needs-unwind +// Reason: unwinding panics is exercised in this test + +//@ ignore-i686-pc-windows-gnu +// Reason: This test doesn't work on 32-bit MinGW as cdylib has its own copy of unwinder +// so cross-DLL unwinding does not work. + +use run_make_support::{run_fail, rustc}; + +fn main() { + rustc().input("bar.rs").crate_type("cdylib").run(); + rustc().input("foo.rs").run(); + run_fail("foo").assert_stderr_contains("Rust cannot catch foreign exceptions"); +} diff --git a/tests/run-make/incr-foreign-head-span/Makefile b/tests/run-make/incr-foreign-head-span/Makefile deleted file mode 100644 index 9be4b0f601c..00000000000 --- a/tests/run-make/incr-foreign-head-span/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -include ../tools.mk - -# ignore-none no-std is not supported -# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for 'std' - -# Ensure that modifying a crate on disk (without recompiling it) -# does not cause ICEs in downstream crates. -# Previously, we would call `SourceMap.guess_head_span` on a span -# from an external crate, which would cause us to read an upstream -# source file from disk during compilation of a downstream crate -# See #86480 for more details - -INCR=$(TMPDIR)/incr - -all: - cp first_crate.rs second_crate.rs $(TMPDIR) - $(RUSTC) $(TMPDIR)/first_crate.rs -C incremental=$(INCR) --target $(TARGET) --crate-type lib - $(RUSTC) $(TMPDIR)/second_crate.rs -C incremental=$(INCR) --target $(TARGET) --extern first_crate=$(TMPDIR)/libfirst_crate.rlib --crate-type lib - rm $(TMPDIR)/first_crate.rs - $(RUSTC) $(TMPDIR)/second_crate.rs -C incremental=$(INCR) --target $(TARGET) --cfg second_run --crate-type lib - diff --git a/tests/run-make/incr-foreign-head-span/rmake.rs b/tests/run-make/incr-foreign-head-span/rmake.rs new file mode 100644 index 00000000000..92e2ed5f879 --- /dev/null +++ b/tests/run-make/incr-foreign-head-span/rmake.rs @@ -0,0 +1,25 @@ +// Ensure that modifying a crate on disk (without recompiling it) +// does not cause ICEs (internal compiler errors) in downstream crates. +// Previously, we would call `SourceMap.guess_head_span` on a span +// from an external crate, which would cause us to read an upstream +// source file from disk during compilation of a downstream crate. +// See https://github.com/rust-lang/rust/issues/86480 + +//@ ignore-none +// Reason: no-std is not supported +//@ ignore-nvptx64-nvidia-cuda +// Reason: can't find crate for 'std' + +use run_make_support::{rfs, rust_lib_name, rustc}; + +fn main() { + rustc().input("first_crate.rs").incremental("incr").crate_type("lib").run(); + rustc() + .input("second_crate.rs") + .incremental("incr") + .extern_("first_crate", rust_lib_name("first_crate")) + .crate_type("lib") + .run(); + rfs::remove_file("first_crate.rs"); + rustc().input("second_crate.rs").incremental("incr").cfg("second_run").crate_type("lib").run(); +} diff --git a/tests/run-make/interdependent-c-libraries/Makefile b/tests/run-make/interdependent-c-libraries/Makefile deleted file mode 100644 index 53a696d82bf..00000000000 --- a/tests/run-make/interdependent-c-libraries/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# The rust crate foo will link to the native library foo, while the rust crate -# bar will link to the native library bar. There is also a dependency between -# the native library bar to the natibe library foo. -# -# This test ensures that the ordering of -lfoo and -lbar on the command line is -# correct to complete the linkage. If passed as "-lfoo -lbar", then the 'foo' -# library will be stripped out, and the linkage will fail. - -all: $(call NATIVE_STATICLIB,foo) $(call NATIVE_STATICLIB,bar) - $(RUSTC) foo.rs - $(RUSTC) bar.rs - $(RUSTC) main.rs --print link-args diff --git a/tests/run-make/interdependent-c-libraries/rmake.rs b/tests/run-make/interdependent-c-libraries/rmake.rs new file mode 100644 index 00000000000..ee8cc76c9cc --- /dev/null +++ b/tests/run-make/interdependent-c-libraries/rmake.rs @@ -0,0 +1,21 @@ +// The rust crate foo will link to the native library foo, while the rust crate +// bar will link to the native library bar. There is also a dependency between +// the native library bar to the natibe library foo. +// This test ensures that the ordering of -lfoo and -lbar on the command line is +// correct to complete the linkage. If passed as "-lfoo -lbar", then the 'foo' +// library will be stripped out, and the linkage will fail. +// See https://github.com/rust-lang/rust/commit/e6072fa0c4c22d62acf3dcb78c8ee260a1368bd7 + +//@ ignore-cross-compile +// Reason: linkage still fails as the object files produced are not in the correct +// format in the `build_native_static_lib` step + +use run_make_support::{build_native_static_lib, rustc}; + +fn main() { + build_native_static_lib("foo"); + build_native_static_lib("bar"); + rustc().input("foo.rs").run(); + rustc().input("bar.rs").run(); + rustc().input("main.rs").print("link-args").run(); +} diff --git a/tests/run-make/panic-abort-eh_frame/Makefile b/tests/run-make/panic-abort-eh_frame/Makefile deleted file mode 100644 index 7020455b742..00000000000 --- a/tests/run-make/panic-abort-eh_frame/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# only-linux -# -# This test ensures that `panic=abort` code (without `C-unwind`, that is) should not have any -# unwinding related `.eh_frame` sections emitted. - -include ../tools.mk - -all: - $(RUSTC) foo.rs --crate-type=lib --emit=obj=$(TMPDIR)/foo.o -Cpanic=abort --edition 2021 -Z validate-mir - objdump --dwarf=frames $(TMPDIR)/foo.o | $(CGREP) -v 'DW_CFA' diff --git a/tests/run-make/panic-abort-eh_frame/rmake.rs b/tests/run-make/panic-abort-eh_frame/rmake.rs new file mode 100644 index 00000000000..23d95dc5774 --- /dev/null +++ b/tests/run-make/panic-abort-eh_frame/rmake.rs @@ -0,0 +1,24 @@ +// An `.eh_frame` section in an object file is a symptom of an UnwindAction::Terminate +// being inserted, useful for determining whether or not unwinding is necessary. +// This is useless when panics would NEVER unwind due to -C panic=abort. This section should +// therefore never appear in the emit file of a -C panic=abort compilation, and this test +// checks that this is respected. +// See https://github.com/rust-lang/rust/pull/112403 + +//@ only-linux +// FIXME(Oneirical): the DW_CFA symbol appears on Windows-gnu, because uwtable +// is forced to true on Windows targets (see #128136). + +use run_make_support::{llvm_objdump, rustc}; + +fn main() { + rustc() + .input("foo.rs") + .crate_type("lib") + .emit("obj=foo.o") + .panic("abort") + .edition("2021") + .arg("-Zvalidate-mir") + .run(); + llvm_objdump().arg("--dwarf=frames").input("foo.o").run().assert_stdout_not_contains("DW_CFA"); +} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs deleted file mode 100644 index 5ac7e1e58b8..00000000000 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ compile-flags: --crate-type=lib -//@ check-pass - -#![feature(inherent_associated_types)] -#![allow(incomplete_features)] - -// Bounds on the self type play a major role in the resolution of inherent associated types (*). -// As a result of that, if a type alias contains any then its bounds have to be respected and the -// lint `type_alias_bounds` should not fire. - -#![deny(type_alias_bounds)] - -pub type Alias<T: Bound> = (Source<T>::Assoc,); - -pub struct Source<T>(T); -pub trait Bound {} - -impl<T: Bound> Source<T> { - pub type Assoc = (); -} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs new file mode 100644 index 00000000000..61641a83994 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs @@ -0,0 +1,29 @@ +//@ compile-flags: --crate-type=lib +//@ check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): +// While we currently do take some clauses of the ParamEnv into consideration +// when performing IAT selection, we do not perform full well-formedness checking +// for (eager) type alias definition and usage sites. +// +// Therefore it's *correct* for lint `type_alias_bounds` to fire here despite the +// fact that removing `Bound` from `T` in `Alias` would lead to an error! +// +// Obviously, the present situation isn't ideal and we should fix it in one way +// or another. Either we somehow delay IAT selection until after HIR ty lowering +// to avoid the need to specify any bounds inside (eager) type aliases or we +// force the overarching type alias to be *lazy* (similar to TAITs) which would +// automatically lead to full wfchecking and lint TAB getting suppressed. + +pub type Alias<T: Bound> = (Source<T>::Assoc,); +//~^ WARN bounds on generic parameters in type aliases are not enforced + +pub struct Source<T>(T); +pub trait Bound {} + +impl<T: Bound> Source<T> { + pub type Assoc = (); +} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr new file mode 100644 index 00000000000..c56dd498f77 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr @@ -0,0 +1,16 @@ +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:21:19 + | +LL | pub type Alias<T: Bound> = (Source<T>::Assoc,); + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics + = note: `#[warn(type_alias_bounds)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/associated-type-bounds/type-alias.stderr b/tests/ui/associated-type-bounds/type-alias.stderr index 072c471467c..d59952b4a14 100644 --- a/tests/ui/associated-type-bounds/type-alias.stderr +++ b/tests/ui/associated-type-bounds/type-alias.stderr @@ -1,147 +1,159 @@ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:3:25 | LL | type _TaWhere1<T> where T: Iterator<Item: Copy> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | + | ------^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere1<T> where T: Iterator<Item: Copy> = T; -LL + type _TaWhere1<T> = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:4:25 | LL | type _TaWhere2<T> where T: Iterator<Item: 'static> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere2<T> where T: Iterator<Item: 'static> = T; -LL + type _TaWhere2<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:5:25 | LL | type _TaWhere3<T> where T: Iterator<Item: 'static> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere3<T> where T: Iterator<Item: 'static> = T; -LL + type _TaWhere3<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:6:25 | LL | type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T; -LL + type _TaWhere4<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:7:25 | LL | type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T; -LL + type _TaWhere5<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:8:25 | LL | type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T; -LL + type _TaWhere6<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:10:20 | LL | type _TaInline1<T: Iterator<Item: Copy>> = T; - | ^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline1<T: Iterator<Item: Copy>> = T; -LL + type _TaInline1<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:11:20 | LL | type _TaInline2<T: Iterator<Item: 'static>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline2<T: Iterator<Item: 'static>> = T; -LL + type _TaInline2<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:12:20 | LL | type _TaInline3<T: Iterator<Item: 'static>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline3<T: Iterator<Item: 'static>> = T; -LL + type _TaInline3<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:13:20 | LL | type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T; -LL + type _TaInline4<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:14:20 | LL | type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T; -LL + type _TaInline5<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:15:20 | LL | type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T; -LL + type _TaInline6<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 12 warnings emitted diff --git a/tests/ui/async-await/async-closures/clone-closure.rs b/tests/ui/async-await/async-closures/clone-closure.rs new file mode 100644 index 00000000000..807897e3e03 --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass +//@ check-run-results + +#![feature(async_closure)] + +extern crate block_on; + +async fn for_each(f: impl async FnOnce(&str) + Clone) { + f.clone()("world").await; + f.clone()("world2").await; +} + +fn main() { + block_on::block_on(async_main()); +} + +async fn async_main() { + let x = String::from("Hello,"); + for_each(async move |s| { + println!("{x} {s}"); + }).await; +} diff --git a/tests/ui/async-await/async-closures/clone-closure.run.stdout b/tests/ui/async-await/async-closures/clone-closure.run.stdout new file mode 100644 index 00000000000..0cfcf1923da --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.run.stdout @@ -0,0 +1,2 @@ +Hello, world +Hello, world2 diff --git a/tests/ui/async-await/async-closures/move-consuming-capture.stderr b/tests/ui/async-await/async-closures/move-consuming-capture.stderr index 45c1eac8f8f..4ce71ec49d6 100644 --- a/tests/ui/async-await/async-closures/move-consuming-capture.stderr +++ b/tests/ui/async-await/async-closures/move-consuming-capture.stderr @@ -11,6 +11,15 @@ LL | x().await; | note: `async_call_once` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL +help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied + | +LL | x.clone()().await; + | ++++++++ +help: consider annotating `NoCopy` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NoCopy; + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-closures/not-clone-closure.rs b/tests/ui/async-await/async-closures/not-clone-closure.rs new file mode 100644 index 00000000000..2776ce4690f --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.rs @@ -0,0 +1,36 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +struct NotClonableArg; +#[derive(Default)] +struct NotClonableReturnType; + +// Verify that the only components that we care about are the upvars, not the signature. +fn we_are_okay_with_not_clonable_signature() { + let x = async |x: NotClonableArg| -> NotClonableReturnType { Default::default() }; + x.clone(); // Okay +} + +#[derive(Debug)] +struct NotClonableUpvar; + +fn we_only_care_about_clonable_upvars() { + let x = NotClonableUpvar; + // Notably, this is clone because we capture `&x`. + let yes_clone = async || { + println!("{x:?}"); + }; + yes_clone.clone(); // Okay + + let z = NotClonableUpvar; + // However, this is not because the closure captures `z` by move. + // (Even though the future that is lent out captures `z by ref!) + let not_clone = async move || { + println!("{z:?}"); + }; + not_clone.clone(); + //~^ ERROR the trait bound `NotClonableUpvar: Clone` is not satisfied +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/not-clone-closure.stderr b/tests/ui/async-await/async-closures/not-clone-closure.stderr new file mode 100644 index 00000000000..aea48a455c2 --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `NotClonableUpvar: Clone` is not satisfied in `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}` + --> $DIR/not-clone-closure.rs:32:15 + | +LL | not_clone.clone(); + | ^^^^^ within `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`, the trait `Clone` is not implemented for `NotClonableUpvar`, which is required by `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}: Clone` + | +note: required because it's used within this closure + --> $DIR/not-clone-closure.rs:29:21 + | +LL | let not_clone = async move || { + | ^^^^^^^^^^^^^ +help: consider annotating `NotClonableUpvar` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClonableUpvar; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs index 17681161e20..18f16ca4b2d 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -19,7 +19,7 @@ fn simple<'a>(x: &'a i32) { let c = async move || { println!("{}", *x); }; outlives::<'a>(c()); //~ ERROR `c` does not live long enough - outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` + outlives::<'a>(call_once(c)); } struct S<'a>(&'a i32); diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index 569028934cb..1df5abdbb18 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -29,22 +29,6 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed -error[E0505]: cannot move out of `c` because it is borrowed - --> $DIR/without-precise-captures-we-are-powerless.rs:22:30 - | -LL | fn simple<'a>(x: &'a i32) { - | -- lifetime `'a` defined here -... -LL | let c = async move || { println!("{}", *x); }; - | - binding `c` declared here -LL | outlives::<'a>(c()); - | --- - | | - | borrow of `c` occurs here - | argument requires that `c` is borrowed for `'a` -LL | outlives::<'a>(call_once(c)); - | ^ move out of `c` occurs here - error[E0597]: `x` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:28:13 | @@ -146,7 +130,7 @@ LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out w LL | } | - `c` dropped here while still borrowed -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0505, E0597, E0621. For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr new file mode 100644 index 00000000000..fa12dd14753 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr @@ -0,0 +1,63 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:23:12 + | +LL | let _: AliasConstUnused<String>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_0::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:20:30 + | +LL | type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>); + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:31:12 + | +LL | let _: AliasConstUnused; + | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_1::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:29:41 + | +LL | type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:39:12 + | +LL | let _: AliasFnUnused<String>; + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUnused` + --> $DIR/type-alias-bounds.rs:36:27 + | +LL | type AliasFnUnused<T: Copy> = (T, I32<{ code() }>); + | ^^^^ required by this bound in `AliasFnUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:57:12 + | +LL | let _: AliasAssocConstUsed<String>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasAssocConstUsed` + --> $DIR/type-alias-bounds.rs:55:41 + | +LL | type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>; + | ^^^^ required by this bound in `AliasAssocConstUsed` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:65:12 + | +LL | let _: AliasFnUsed<String>; + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUsed` + --> $DIR/type-alias-bounds.rs:62:33 + | +LL | type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>; + | ^^^^ required by this bound in `AliasFnUsed` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs new file mode 100644 index 00000000000..f16e646129c --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs @@ -0,0 +1,71 @@ +//@ revisions: pos neg +//@[pos] check-pass + +#![feature(generic_const_exprs)] +#![feature(trivial_bounds)] // only used in test case `ct_unused_1` +#![allow(incomplete_features)] + +// FIXME(generic_const_exprs): Revisit this before stabilization. +// Check that we don't emit the lint `type_alias_bounds` for (eager) type aliases +// whose RHS contains a const projection (aka uneval'ed const). +// Since anon consts inherit the parent generics and predicates and we effectively +// check them before and after instantiaton for well-formedness, the type alias +// bounds are in every sense "enforced". +// Note that the test cases whose name ends in "unused" just demonstrate that this +// holds even if the const projections don't "visibly" capture any generics and/or +// predicates. +#![deny(type_alias_bounds)] + +fn ct_unused_0() { + type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>); + const DATA: i32 = 0; + #[cfg(neg)] + let _: AliasConstUnused<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn ct_unused_1() { + #[allow(trivial_bounds)] + type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + #[cfg(neg)] + let _: AliasConstUnused; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_unused() { + type AliasFnUnused<T: Copy> = (T, I32<{ code() }>); + const fn code() -> i32 { 0 } + #[cfg(neg)] + let _: AliasFnUnused<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +trait Trait { + type Proj; + const DATA: i32; +} + +impl Trait for String { + type Proj = i32; + const DATA: i32 = 0; +} + +// Regression test for issue #94398. +fn assoc_ct_used() { + type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>; + #[cfg(neg)] + let _: AliasAssocConstUsed<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_used() { + type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>; + const fn code<T: Trait>() -> i32 { T::DATA } + #[cfg(neg)] + let _: AliasFnUsed<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +struct I32<const N: i32>; + +fn main() {} diff --git a/tests/ui/consts/const-eval/parse_ints.rs b/tests/ui/consts/const-eval/parse_ints.rs index ff9fc47e65c..cb9a3eb4312 100644 --- a/tests/ui/consts/const-eval/parse_ints.rs +++ b/tests/ui/consts/const-eval/parse_ints.rs @@ -1,5 +1,3 @@ -#![feature(const_int_from_str)] - const _OK: () = match i32::from_str_radix("-1234", 10) { Ok(x) => assert!(x == -1234), Err(_) => panic!(), diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index 9e49fe433a1..ec9249ece8e 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -6,7 +6,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::<impl u64>::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_LOW` - --> $DIR/parse_ints.rs:7:24 + --> $DIR/parse_ints.rs:5:24 | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::<impl u64>::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_HIGH` - --> $DIR/parse_ints.rs:8:25 + --> $DIR/parse_ints.rs:6:25 | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs new file mode 100644 index 00000000000..debe143b091 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -0,0 +1,24 @@ +#![feature(auto_traits)] + +trait Trait1 {} +auto trait Trait2 {} +trait Trait3: ?Trait1 {} +//~^ ERROR `?Trait` is not permitted in supertraits +trait Trait4 where Self: ?Trait1 {} +//~^ ERROR ?Trait` bounds are only permitted at the point where a type parameter is declared + +fn foo(_: Box<dyn Trait1 + ?Trait2>) {} +//~^ ERROR `?Trait` is not permitted in trait object types +fn bar<T: ?Trait1 + ?Trait2>(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +trait Trait {} +// Do not suggest `#![feature(more_maybe_bounds)]` for repetitions +fn baz<T: ?Trait + ?Trait>(_ : T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr new file mode 100644 index 00000000000..e6d65e05997 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -0,0 +1,71 @@ +error[E0658]: `?Trait` is not permitted in supertraits + --> $DIR/feature-gate-more-maybe-bounds.rs:5:15 + | +LL | trait Trait3: ?Trait1 {} + | ^^^^^^^ + | + = note: traits are `?Trait1` by default + = help: add `#![feature(more_maybe_bounds)]` 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]: `?Trait` is not permitted in trait object types + --> $DIR/feature-gate-more-maybe-bounds.rs:10:28 + | +LL | fn foo(_: Box<dyn Trait1 + ?Trait2>) {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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]: `?Trait` bounds are only permitted at the point where a type parameter is declared + --> $DIR/feature-gate-more-maybe-bounds.rs:7:26 + | +LL | trait Trait4 where Self: ?Trait1 {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {} + | ^^^^^^^ ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:21 + | +LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {} + | ^^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz<T: ?Trait + ?Trait>(_ : T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz<T: ?Trait + ?Trait>(_ : T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:20 + | +LL | fn baz<T: ?Trait + ?Trait>(_ : T) {} + | ^^^^^^ + +error: aborting due to 5 previous errors; 4 warnings emitted + +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/lang-items/missing-clone-for-suggestion.rs b/tests/ui/lang-items/missing-clone-for-suggestion.rs deleted file mode 100644 index e8290c0098a..00000000000 --- a/tests/ui/lang-items/missing-clone-for-suggestion.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Avoid panicking if the Clone trait is not found while building error suggestions -// See #104870 - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -trait Sized {} - -#[lang = "copy"] -trait Copy {} - -fn g<T>(x: T) {} - -fn f(x: *mut u8) { - g(x); - g(x); //~ ERROR use of moved value: `x` -} - -fn main() {} diff --git a/tests/ui/lang-items/missing-clone-for-suggestion.stderr b/tests/ui/lang-items/missing-clone-for-suggestion.stderr deleted file mode 100644 index 0187f965b5c..00000000000 --- a/tests/ui/lang-items/missing-clone-for-suggestion.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0382]: use of moved value: `x` - --> $DIR/missing-clone-for-suggestion.rs:17:7 - | -LL | fn f(x: *mut u8) { - | - move occurs because `x` has type `*mut u8`, which does not implement the `Copy` trait -LL | g(x); - | - value moved here -LL | g(x); - | ^ value used here after move - | -note: consider changing this parameter type in function `g` to borrow instead if owning the value isn't necessary - --> $DIR/missing-clone-for-suggestion.rs:13:12 - | -LL | fn g<T>(x: T) {} - | - ^ this parameter takes ownership of the value - | | - | in this function - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs new file mode 100644 index 00000000000..46065dcee81 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs @@ -0,0 +1,45 @@ +#![deny(dead_code)] + +struct Foo; //~ ERROR struct `Foo` is never constructed + +trait Trait { //~ ERROR trait `Trait` is never used + fn foo(&self) {} +} + +impl Trait for Foo {} + +impl Trait for [Foo] {} +impl<const N: usize> Trait for [Foo; N] {} + +impl Trait for *const Foo {} +impl Trait for *mut Foo {} + +impl Trait for &Foo {} +impl Trait for &&Foo {} +impl Trait for &mut Foo {} + +impl Trait for [&Foo] {} +impl Trait for &[Foo] {} +impl Trait for &*const Foo {} + +pub trait Trait2 { + fn foo(&self) {} +} + +impl Trait2 for Foo {} + +impl Trait2 for [Foo] {} +impl<const N: usize> Trait2 for [Foo; N] {} + +impl Trait2 for *const Foo {} +impl Trait2 for *mut Foo {} + +impl Trait2 for &Foo {} +impl Trait2 for &&Foo {} +impl Trait2 for &mut Foo {} + +impl Trait2 for [&Foo] {} +impl Trait2 for &[Foo] {} +impl Trait2 for &*const Foo {} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr new file mode 100644 index 00000000000..e61fc403e81 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr @@ -0,0 +1,20 @@ +error: struct `Foo` is never constructed + --> $DIR/unused-impl-for-non-adts.rs:3:8 + | +LL | struct Foo; + | ^^^ + | +note: the lint level is defined here + --> $DIR/unused-impl-for-non-adts.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: trait `Trait` is never used + --> $DIR/unused-impl-for-non-adts.rs:5:7 + | +LL | trait Trait { + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs index 97454c9de07..a41372e4ea8 100644 --- a/tests/ui/macros/issue-118786.rs +++ b/tests/ui/macros/issue-118786.rs @@ -7,6 +7,7 @@ macro_rules! make_macro { macro_rules! $macro_name { //~^ ERROR macro expansion ignores token `{` and any following //~| ERROR cannot find macro `macro_rules` in this scope + //~| put a macro name here () => {} } } diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr index 03e65c94ba7..256b742ee16 100644 --- a/tests/ui/macros/issue-118786.stderr +++ b/tests/ui/macros/issue-118786.stderr @@ -1,5 +1,5 @@ error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-118786.rs:15:13 + --> $DIR/issue-118786.rs:16:13 | LL | make_macro!((meow)); | ^^^^^^ @@ -34,10 +34,10 @@ LL | make_macro!((meow)); | ------------------- in this macro invocation | note: maybe you have forgotten to define a name for this `macro_rules!` - --> $DIR/issue-118786.rs:7:9 + --> $DIR/issue-118786.rs:7:20 | LL | macro_rules! $macro_name { - | ^^^^^^^^^^^ + | ^ put a macro name here ... LL | make_macro!((meow)); | ------------------- in this macro invocation diff --git a/tests/ui/maybe-bounds.stderr b/tests/ui/maybe-bounds.stderr index 1d823b6acb2..230d11fd0ae 100644 --- a/tests/ui/maybe-bounds.stderr +++ b/tests/ui/maybe-bounds.stderr @@ -1,22 +1,31 @@ -error: `?Trait` is not permitted in supertraits +error[E0658]: `?Trait` is not permitted in supertraits --> $DIR/maybe-bounds.rs:1:11 | LL | trait Tr: ?Sized {} | ^^^^^^ | = note: traits are `?Sized` by default + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:4:20 | LL | type A1 = dyn Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:6:28 | LL | type A2 = dyn for<'a> Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/issues/issue-19398.rs b/tests/ui/parser/issues/issue-19398.rs index 46eb320a172..358f65f1da5 100644 --- a/tests/ui/parser/issues/issue-19398.rs +++ b/tests/ui/parser/issues/issue-19398.rs @@ -1,6 +1,10 @@ trait T { extern "Rust" unsafe fn foo(); - //~^ ERROR expected `{`, found keyword `unsafe` + //~^ ERROR expected `fn`, found keyword `unsafe` + //~| NOTE expected `fn` + //~| HELP `unsafe` must come before `extern "Rust"` + //~| SUGGESTION unsafe extern "Rust" + //~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` } fn main() {} diff --git a/tests/ui/parser/issues/issue-19398.stderr b/tests/ui/parser/issues/issue-19398.stderr index 236fac673b6..2b97ec50c91 100644 --- a/tests/ui/parser/issues/issue-19398.stderr +++ b/tests/ui/parser/issues/issue-19398.stderr @@ -1,13 +1,13 @@ -error: expected `{`, found keyword `unsafe` +error: expected `fn`, found keyword `unsafe` --> $DIR/issue-19398.rs:2:19 | -LL | trait T { - | - while parsing this item list starting here LL | extern "Rust" unsafe fn foo(); - | ^^^^^^ expected `{` -LL | -LL | } - | - the item list ends here + | --------------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "Rust"`: `unsafe extern "Rust"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs new file mode 100644 index 00000000000..794ac635c76 --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs @@ -0,0 +1,16 @@ +//@ edition:2018 + +// There is an order to respect for keywords before a function: +// `<visibility>, const, async, unsafe, extern, "<ABI>"` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +extern "C" unsafe fn test() {} +//~^ ERROR expected `fn`, found keyword `unsafe` +//~| NOTE expected `fn` +//~| HELP `unsafe` must come before `extern "C"` +//~| SUGGESTION unsafe extern "C" +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` + +fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr new file mode 100644 index 00000000000..8ed037869c8 --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr @@ -0,0 +1,13 @@ +error: expected `fn`, found keyword `unsafe` + --> $DIR/wrong-unsafe-abi.rs:9:12 + | +LL | extern "C" unsafe fn test() {} + | -----------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "C"`: `unsafe extern "C"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/trait-object-trait-parens.stderr b/tests/ui/parser/trait-object-trait-parens.stderr index 3134746b930..ff32b173d49 100644 --- a/tests/ui/parser/trait-object-trait-parens.stderr +++ b/tests/ui/parser/trait-object-trait-parens.stderr @@ -1,20 +1,29 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:8:24 | LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:13:16 | LL | let _: Box<?Sized + (for<'a> Trait<'a>) + (Obj)>; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:18:44 | LL | let _: Box<for<'a> Trait<'a> + (Obj) + (?Sized)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: trait objects without an explicit `dyn` are deprecated --> $DIR/trait-object-trait-parens.rs:8:16 @@ -91,4 +100,5 @@ LL | let _: Box<for<'a> Trait<'a> + (Obj) + (?Sized)>; error: aborting due to 6 previous errors; 3 warnings emitted -For more information about this error, try `rustc --explain E0225`. +Some errors have detailed explanations: E0225, E0658. +For more information about an error, try `rustc --explain E0225`. diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs index 99d318e36be..746b98fbd07 100644 --- a/tests/ui/privacy/private-in-public-warn.rs +++ b/tests/ui/privacy/private-in-public-warn.rs @@ -39,7 +39,7 @@ mod traits { pub trait PubTr {} pub type Alias<T: PrivTr> = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias` - //~^ WARNING bounds on generic parameters are not enforced in type aliases + //~^ WARNING bounds on generic parameters in type aliases are not enforced pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1` pub trait Tr2<T: PrivTr> {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2` pub trait Tr3 { @@ -58,7 +58,7 @@ mod traits_where { pub type Alias<T> where T: PrivTr = T; //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias` - //~| WARNING where clauses are not enforced in type aliases + //~| WARNING where clauses on type aliases are not enforced pub trait Tr2<T> where T: PrivTr {} //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2` pub trait Tr3 { diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr index ac7e5547de9..3f7b8c281e7 100644 --- a/tests/ui/privacy/private-in-public-warn.stderr +++ b/tests/ui/privacy/private-in-public-warn.stderr @@ -395,30 +395,32 @@ note: but type `Priv2` is only usable at visibility `pub(self)` LL | struct Priv2; | ^^^^^^^^^^^^ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/private-in-public-warn.rs:41:23 | LL | pub type Alias<T: PrivTr> = T; - | ^^^^^^ - | + | --^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias<T: PrivTr> = T; -LL + pub type Alias<T> = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/private-in-public-warn.rs:59:29 | LL | pub type Alias<T> where T: PrivTr = T; - | ^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias<T> where T: PrivTr = T; -LL + pub type Alias<T> = T; - | + | ------^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics error: aborting due to 34 previous errors; 2 warnings emitted diff --git a/tests/ui/resolve/issue-118295.rs b/tests/ui/resolve/issue-118295.rs index b97681d9563..37a49baee80 100644 --- a/tests/ui/resolve/issue-118295.rs +++ b/tests/ui/resolve/issue-118295.rs @@ -1,5 +1,11 @@ macro_rules! {} //~^ ERROR cannot find macro `macro_rules` in this scope //~| NOTE maybe you have forgotten to define a name for this `macro_rules!` +//~| put a macro name here + +macro_rules!{} +//~^ ERROR cannot find macro `macro_rules` in this scope +//~| NOTE maybe you have forgotten to define a name for this `macro_rules!` +//~| put a macro name here fn main() {} diff --git a/tests/ui/resolve/issue-118295.stderr b/tests/ui/resolve/issue-118295.stderr index d60d7d9185d..06a37e81d6c 100644 --- a/tests/ui/resolve/issue-118295.stderr +++ b/tests/ui/resolve/issue-118295.stderr @@ -1,14 +1,26 @@ error: cannot find macro `macro_rules` in this scope - --> $DIR/issue-118295.rs:1:1 + --> $DIR/issue-118295.rs:6:1 | -LL | macro_rules! {} +LL | macro_rules!{} | ^^^^^^^^^^^ | note: maybe you have forgotten to define a name for this `macro_rules!` + --> $DIR/issue-118295.rs:6:12 + | +LL | macro_rules!{} + | ^ put a macro name here + +error: cannot find macro `macro_rules` in this scope --> $DIR/issue-118295.rs:1:1 | LL | macro_rules! {} | ^^^^^^^^^^^ + | +note: maybe you have forgotten to define a name for this `macro_rules!` + --> $DIR/issue-118295.rs:1:12 + | +LL | macro_rules! {} + | ^ put a macro name here -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index ffc3252230a..4069b35a998 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -19,7 +19,7 @@ help: consider further restricting type parameter `T` | LL | T::Baa: std::fmt::Debug, T: Foo | ~~~~~~~~ -help: and also change the associated type name +help: ...and changing the associated type name | LL | T::Bar: std::fmt::Debug, | ~~~ diff --git a/tests/ui/traits/maybe-polarity-pass.rs b/tests/ui/traits/maybe-polarity-pass.rs new file mode 100644 index 00000000000..075a0d8dcac --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![feature(auto_traits)] +#![feature(more_maybe_bounds)] +#![feature(negative_impls)] + +trait Trait1 {} +auto trait Trait2 {} + +trait Trait3 : ?Trait1 {} +trait Trait4 where Self: Trait1 {} + +fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} +fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} +//~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +struct S; +impl !Trait2 for S {} +impl Trait1 for S {} +impl Trait3 for S {} + +fn main() { + foo(Box::new(S)); + bar(&S); +} diff --git a/tests/ui/traits/maybe-polarity-pass.stderr b/tests/ui/traits/maybe-polarity-pass.stderr new file mode 100644 index 00000000000..09ed52f5b0d --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.stderr @@ -0,0 +1,20 @@ +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:20 + | +LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:30 + | +LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:40 + | +LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} + | ^^^^^^^ + +warning: 3 warnings emitted + diff --git a/tests/ui/traits/maybe-polarity-repeated.rs b/tests/ui/traits/maybe-polarity-repeated.rs new file mode 100644 index 00000000000..4b5ec83fffa --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.rs @@ -0,0 +1,9 @@ +#![feature(more_maybe_bounds)] + +trait Trait {} +fn foo<T: ?Trait + ?Trait>(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/traits/maybe-polarity-repeated.stderr b/tests/ui/traits/maybe-polarity-repeated.stderr new file mode 100644 index 00000000000..610c484fbec --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.stderr @@ -0,0 +1,21 @@ +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo<T: ?Trait + ?Trait>(_: T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo<T: ?Trait + ?Trait>(_: T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:20 + | +LL | fn foo<T: ?Trait + ?Trait>(_: T) {} + | ^^^^^^ + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0203`. diff --git a/tests/ui/traits/wf-object/maybe-bound.stderr b/tests/ui/traits/wf-object/maybe-bound.stderr index 2fe3f0fc39f..be7afabd0d0 100644 --- a/tests/ui/traits/wf-object/maybe-bound.stderr +++ b/tests/ui/traits/wf-object/maybe-bound.stderr @@ -1,32 +1,48 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:5:15 | LL | type _0 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:8:21 | LL | type _1 = dyn Foo + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:21 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:30 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:15:15 | LL | type _3 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/wf-object/only-maybe-bound.stderr b/tests/ui/traits/wf-object/only-maybe-bound.stderr index cbc41feec1e..26269476eaa 100644 --- a/tests/ui/traits/wf-object/only-maybe-bound.stderr +++ b/tests/ui/traits/wf-object/only-maybe-bound.stderr @@ -1,8 +1,11 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/only-maybe-bound.rs:3:15 | LL | type _0 = dyn ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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[E0224]: at least one trait is required for an object type --> $DIR/only-maybe-bound.rs:3:11 @@ -12,4 +15,5 @@ LL | type _0 = dyn ?Sized; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0224`. +Some errors have detailed explanations: E0224, E0658. +For more information about an error, try `rustc --explain E0224`. diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr index d66e468873b..0eae68bfcf0 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr @@ -24,18 +24,19 @@ warning: trait bound i32: Foo does not depend on any type or lifetime parameters LL | union U where i32: Foo { f: i32 } | ^^^ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/trivial-bounds-inconsistent.rs:22:14 | LL | type Y where i32: Foo = (); - | ^^^^^^^^ - | + | ------^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type Y where i32: Foo = (); -LL + type Y = (); - | warning: trait bound i32: Foo does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:22:19 diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr new file mode 100644 index 00000000000..e891ff10fda --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -0,0 +1,36 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 + | +LL | type AssocOf<T> = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying the associated type + | +LL | type AssocOf<T> = <T as Trait>::Assoc; + | + +++++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 + | +LL | type AssokOf<T> = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying and renaming the associated type + | +LL | type AssokOf<T> = <T as Trait>::Assoc; + | + +++++++++ ~~~~~ + +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf<T> = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider fully qualifying the associated type + | +LL | type ProjOf<T> = <T as Parametrized</* 'a, T, N */>>::Proj; + | + ++++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr new file mode 100644 index 00000000000..96179a7b484 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -0,0 +1,40 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 + | +LL | type AssocOf<T> = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssocOf<T: Trait> = T::Assoc; + | +++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 + | +LL | type AssokOf<T> = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssokOf<T: Trait> = T::Assok; + | +++++++ +help: ...and changing the associated type name + | +LL | type AssokOf<T> = T::Assoc; + | ~~~~~ + +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf<T> = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider restricting type parameter `T` + | +LL | type ProjOf<T: Parametrized</* 'a, T, N */>> = T::Proj; + | ++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs new file mode 100644 index 00000000000..2c8d448f308 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -0,0 +1,26 @@ +// Ensure that we don't suggest *type alias bounds* for **eager** type aliases. +// issue: rust-lang/rust#125789 + +//@ revisions: eager lazy +#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] + +trait Trait { type Assoc; } + +type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + +type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T` +//[eager]~^ HELP consider fully qualifying and renaming the associated type +//[lazy]~| HELP consider restricting type parameter `T` +//[lazy]~| HELP and changing the associated type name + +trait Parametrized<'a, T, const N: usize> { + type Proj; +} + +type ProjOf<T> = T::Proj; //~ ERROR associated type `Proj` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + +fn main() {} diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs index 5ee3c027f40..52e0887175d 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs @@ -3,6 +3,6 @@ //@ check-pass pub type T<P: Send + Send + Send> = P; -//~^ WARN bounds on generic parameters are not enforced in type aliases +//~^ WARN bounds on generic parameters in type aliases are not enforced fn main() {} diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr index 125ffbbb417..9fd0fe4913b 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr @@ -1,15 +1,16 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15 | LL | pub type T<P: Send + Send + Send> = P; - | ^^^^ ^^^^ ^^^^ + | --^^^^---^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type T<P: Send + Send + Send> = P; -LL + pub type T<P> = P; - | warning: 1 warning emitted diff --git a/tests/ui/type/type-alias-bounds.rs b/tests/ui/type/type-alias-bounds.rs index 6d63c0c7e1b..37c073fe1f9 100644 --- a/tests/ui/type/type-alias-bounds.rs +++ b/tests/ui/type/type-alias-bounds.rs @@ -6,15 +6,15 @@ use std::rc::Rc; type SVec<T: Send + Send> = Vec<T>; -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type S2Vec<T> where T: Send = Vec<T>; -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] static STATIC: u32 = 0; @@ -42,10 +42,11 @@ fn foo<'a>(y: &'a i32) { struct Sendable<T: Send>(T); type MySendable<T> = Sendable<T>; // no error here! -// However, bounds *are* taken into account when accessing associated types +// Bounds on type params do enable shorthand type alias paths. +// However, that doesn't actually mean that they are properly enforced. trait Bound { type Assoc; } -type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases -type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases +type T1<U: Bound> = U::Assoc; //~ WARN are not enforced +type T2<U> where U: Bound = U::Assoc; //~ WARN are not enforced // This errors: // `type T3<U> = U::Assoc;` @@ -53,7 +54,7 @@ type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases type T4<U> = <U as Bound>::Assoc; // Make sure the help about associated types is not shown incorrectly -type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases -type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases +type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN are not enforced +type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN are not enforced fn main() {} diff --git a/tests/ui/type/type-alias-bounds.stderr b/tests/ui/type/type-alias-bounds.stderr index 92e573393c9..15c00901066 100644 --- a/tests/ui/type/type-alias-bounds.stderr +++ b/tests/ui/type/type-alias-bounds.stderr @@ -1,121 +1,132 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:8:14 | LL | type SVec<T: Send + Send> = Vec<T>; - | ^^^^ ^^^^ - | + | --^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type SVec<T: Send + Send> = Vec<T>; -LL + type SVec<T> = Vec<T>; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:10:21 | LL | type S2Vec<T> where T: Send = Vec<T>; - | ^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type S2Vec<T> where T: Send = Vec<T>; -LL + type S2Vec<T> = Vec<T>; - | + | ------^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:12:19 | LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -LL + type VVec<'b, 'a> = (&'b u32, Vec<&'a i32>); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:14:18 | LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); -LL + type WVec<'b, T> = (&'b u32, Vec<T>); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:16:25 | LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); - | ^^^^^ ^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); -LL + type W2Vec<'b, T> = (&'b u32, Vec<T>); - | + | ------^^^^^--^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:47:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:48:12 | LL | type T1<U: Bound> = U::Assoc; - | ^^^^^ + | ^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:47:21 - | -LL | type T1<U: Bound> = U::Assoc; - | ^^^^^^^^ -help: the bound will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this bound | LL - type T1<U: Bound> = U::Assoc; LL + type T1<U> = U::Assoc; | +help: fully qualify this associated type + | +LL | type T1<U: Bound> = <U as /* Trait */>::Assoc; + | + +++++++++++++++ -warning: where clauses are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:48:18 +warning: where clauses on type aliases are not enforced + --> $DIR/type-alias-bounds.rs:49:18 | LL | type T2<U> where U: Bound = U::Assoc; - | ^^^^^^^^ + | ^^^^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:48:29 - | -LL | type T2<U> where U: Bound = U::Assoc; - | ^^^^^^^^ -help: the clause will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this where clause | LL - type T2<U> where U: Bound = U::Assoc; LL + type T2<U> = U::Assoc; | +help: fully qualify this associated type + | +LL | type T2<U> where U: Bound = <U as /* Trait */>::Assoc; + | + +++++++++++++++ -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:56:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:57:12 | LL | type T5<U: Bound> = <U as Bound>::Assoc; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T5<U: Bound> = <U as Bound>::Assoc; -LL + type T5<U> = <U as Bound>::Assoc; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:57:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:58:12 | LL | type T6<U: Bound> = ::std::vec::Vec<U>; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T6<U: Bound> = ::std::vec::Vec<U>; -LL + type T6<U> = ::std::vec::Vec<U>; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 9 warnings emitted diff --git a/tests/ui/unsized/maybe-bounds-where.stderr b/tests/ui/unsized/maybe-bounds-where.stderr index 683bd387bb2..f7851261667 100644 --- a/tests/ui/unsized/maybe-bounds-where.stderr +++ b/tests/ui/unsized/maybe-bounds-where.stderr @@ -1,32 +1,47 @@ -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:1:28 | LL | struct S1<T>(T) where (T): ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:4:27 | LL | struct S2<T>(T) where u8: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:7:35 | LL | struct S3<T>(T) where &'static T: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:12:34 | LL | struct S4<T>(T) where for<'a> T: ?Trait<'a>; | ^^^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` 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: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:21:21 | LL | fn f() where T: ?Sized {} | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:12:34 @@ -39,6 +54,9 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i | LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:16:33 @@ -48,4 +66,5 @@ LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized; error: aborting due to 6 previous errors; 2 warnings emitted -For more information about this error, try `rustc --explain E0203`. +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. |
