diff options
39 files changed, 571 insertions, 56 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e33cb7b457..92cc19edb05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ document. ## Rust 1.72 -Current beta, released 2023-08-24 +Current stable, released 2023-08-24 [View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster) @@ -101,7 +101,7 @@ Current beta, released 2023-08-24 ## Rust 1.71 -Current stable, released 2023-07-13 +Released 2023-07-13 Note: Clippy will use a shorter changelog format from now on, if you want a detailed list of all changes, please check out the list of merged pull requests. @@ -4985,6 +4985,7 @@ Released 2018-09-13 [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub +[`implied_bounds_in_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#implied_bounds_in_impls [`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping diff --git a/Cargo.toml b/Cargo.toml index 1ab6d7e118f..cd04c78ef9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.73" +version = "0.1.74" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 11136867ff0..c0a9f466e7b 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.73" +version = "0.1.74" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 5bf467efa0f..1de69122101 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::CAST_PTR_ALIGNMENT; @@ -76,13 +77,14 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { static PATHS: &[&[&str]] = &[ paths::PTR_READ_UNALIGNED.as_slice(), - paths::PTR_WRITE_UNALIGNED.as_slice(), paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(), paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(), ]; + if let ExprKind::Path(path) = &func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_any_def_paths(cx, def_id, PATHS).is_some() + && (match_any_def_paths(cx, def_id, PATHS).is_some() + || cx.tcx.is_diagnostic_item(sym::ptr_write_unaligned, def_id)) { true } else { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1be9720fbbf..f73f1ed18f8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -209,6 +209,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_return::IMPLICIT_RETURN_INFO, crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO, crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index bc011a6c354..58c27855000 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -802,7 +802,8 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo match parent.kind { ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _) if child.hir_id == e.hir_id => true, - ExprKind::Field(_, _) | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) => true, + ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar) + | ExprKind::Field(_, _) => true, _ => false, } } else { diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs new file mode 100644 index 00000000000..c6d1acad3a0 --- /dev/null +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -0,0 +1,202 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem, + TraitItemKind, TyKind, +}; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, ClauseKind, TyCtxt}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Looks for bounds in `impl Trait` in return position that are implied by other bounds. + /// This can happen when a trait is specified that another trait already has as a supertrait + /// (e.g. `fn() -> impl Deref + DerefMut<Target = i32>` has an unnecessary `Deref` bound, + /// because `Deref` is a supertrait of `DerefMut`) + /// + /// ### Why is this bad? + /// Specifying more bounds than necessary adds needless complexity for the reader. + /// + /// ### Limitations + /// This lint does not check for implied bounds transitively. Meaning that + /// it does't check for implied bounds from supertraits of supertraits + /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`) + /// + /// ### Example + /// ```rust + /// # use std::ops::{Deref,DerefMut}; + /// fn f() -> impl Deref<Target = i32> + DerefMut<Target = i32> { + /// // ^^^^^^^^^^^^^^^^^^^ unnecessary bound, already implied by the `DerefMut` trait bound + /// Box::new(123) + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::ops::{Deref,DerefMut}; + /// fn f() -> impl DerefMut<Target = i32> { + /// Box::new(123) + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub IMPLIED_BOUNDS_IN_IMPLS, + complexity, + "specifying bounds that are implied by other bounds in `impl Trait` type" +} +declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); + +/// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`, +/// check if the substituted type in the implied-by bound matches with what's subtituted in the +/// implied type. +/// +/// Consider this example. +/// ```rust,ignore +/// trait GenericTrait<T> {} +/// trait GenericSubTrait<T, U, V>: GenericTrait<U> {} +/// ^ trait_predicate_args: [Self#0, U#2] +/// impl GenericTrait<i32> for () {} +/// impl GenericSubTrait<(), i32, ()> for () {} +/// impl GenericSubTrait<(), [u8; 8], ()> for () {} +/// +/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> { +/// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args +/// (we are interested in `[u8; 8]` specifically, as that +/// is what `U` in `GenericTrait<U>` is substituted with) +/// () +/// } +/// ``` +/// Here i32 != [u8; 8], so this will return false. +fn is_same_generics( + tcx: TyCtxt<'_>, + trait_predicate_args: &[ty::GenericArg<'_>], + implied_by_args: &[GenericArg<'_>], + implied_args: &[GenericArg<'_>], +) -> bool { + trait_predicate_args + .iter() + .enumerate() + .skip(1) // skip `Self` implicit arg + .all(|(arg_index, arg)| { + if let Some(ty) = arg.as_type() + && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind() + // Since `trait_predicate_args` and type params in traits start with `Self=0` + // and generic argument lists `GenericTrait<i32>` don't have `Self`, + // we need to subtract 1 from the index. + && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1] + && let GenericArg::Type(ty_b) = implied_args[arg_index - 1] + { + hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b) + } else { + false + } + }) +} + +fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { + if let FnRetTy::Return(ty) = decl.output + &&let TyKind::OpaqueDef(item_id, ..) = ty.kind + && let item = cx.tcx.hir().item(item_id) + && let ItemKind::OpaqueTy(opaque_ty) = item.kind + // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case + // we can avoid doing a bunch of stuff unnecessarily. + && opaque_ty.bounds.len() > 1 + { + // Get all the (implied) trait predicates in the bounds. + // For `impl Deref + DerefMut` this will contain [`Deref`]. + // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`. + // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits. + // Example: + // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed. + // `DerefMut::Target` needs to match `Deref::Target`. + let implied_bounds: Vec<_> = opaque_ty.bounds.iter().filter_map(|bound| { + if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound + && let [.., path] = poly_trait.trait_ref.path.segments + && poly_trait.bound_generic_params.is_empty() + && let Some(trait_def_id) = path.res.opt_def_id() + && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates + && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add. + { + Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates)) + } else { + None + } + }).collect(); + + // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec. + // This involves some extra logic when generic arguments are present, since + // simply comparing trait `DefId`s won't be enough. We also need to compare the generics. + for (index, bound) in opaque_ty.bounds.iter().enumerate() { + if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound + && let [.., path] = poly_trait.trait_ref.path.segments + && let implied_args = path.args.map_or([].as_slice(), |a| a.args) + && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() + && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| { + preds.iter().find_map(|(clause, _)| { + if let ClauseKind::Trait(tr) = clause.kind().skip_binder() + && tr.def_id() == def_id + && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args) + { + Some(span) + } else { + None + } + }) + }) + { + let implied_by = snippet(cx, implied_by_span, ".."); + span_lint_and_then( + cx, IMPLIED_BOUNDS_IN_IMPLS, + poly_trait.span, + &format!("this bound is already specified as the supertrait of `{implied_by}`"), + |diag| { + // If we suggest removing a bound, we may also need extend the span + // to include the `+` token, so we don't end up with something like `impl + B` + + let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { + poly_trait.span.to(next_bound.span().shrink_to_lo()) + } else { + poly_trait.span + }; + + diag.span_suggestion_with_style( + implied_span_extended, + "try removing this bound", + "", + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways + ); + } + ); + } + } + } +} + +impl LateLintPass<'_> for ImpliedBoundsInImpls { + fn check_fn( + &mut self, + cx: &LateContext<'_>, + _: FnKind<'_>, + decl: &FnDecl<'_>, + _: &Body<'_>, + _: Span, + _: LocalDefId, + ) { + check(cx, decl); + } + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + if let TraitItemKind::Fn(sig, ..) = &item.kind { + check(cx, sig.decl); + } + } + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { + if let ImplItemKind::Fn(sig, ..) = &item.kind { + check(cx, sig.decl); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f50019f3cf7..ab71bfbdc58 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -152,6 +152,7 @@ mod implicit_hasher; mod implicit_return; mod implicit_saturating_add; mod implicit_saturating_sub; +mod implied_bounds_in_impls; mod inconsistent_struct_constructor; mod incorrect_impls; mod index_refutable_slice; @@ -1097,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, 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()); + store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 6d16d188754..930386a60aa 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1038,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { wild_in_or_pats::check(cx, arms); } - if source == MatchSource::TryDesugar { + if let MatchSource::TryDesugar(_) = source { try_err::check(cx, expr, ex); } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 99a748489b4..0fd6f533db0 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -80,7 +80,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine /// Finds function return type by examining return expressions in match arms. fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> { - if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) = expr { for arm in *arms { if let ExprKind::Ret(Some(ret)) = arm.body.kind { return Some(cx.typeck_results().expr_ty(ret)); diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7eb325ee7b5..eb4f003d38a 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -64,7 +64,7 @@ pub(super) fn check( ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _)) ), ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true, - ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + ExprKind::Match(_, _, MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar) | ExprKind::Field(..) | ExprKind::Index(..) => true, _ => false, diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index ee405a3e30c..a49dd98db87 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -21,7 +21,7 @@ pub(super) enum Op<'a> { RmCloned, // rm `.cloned()` - // e.g. `map` `for_each` + // e.g. `map` `for_each` `all` `any` NeedlessMove(&'a str, &'a Expr<'a>), // later `.cloned()` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5075137caa0..5fb0d9f0a57 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3873,6 +3873,12 @@ impl Methods { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); }, + ("all", [arg]) => { + if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, + iter_overeager_cloned::Op::NeedlessMove(name, arg), false); + } + } ("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); @@ -3880,12 +3886,16 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params - && let Some(("chars", recv, _, _, _)) = method_call(recv) => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + ("any", [arg]) => { + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false), + Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } + _ => {} + } } ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 41986551da4..7016ad0a80f 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -236,7 +236,7 @@ fn indirect_usage<'tcx>( !matches!( node, Node::Expr(Expr { - kind: ExprKind::Match(.., MatchSource::TryDesugar), + kind: ExprKind::Match(.., MatchSource::TryDesugar(_)), .. }) ) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index a41d5a9ce8d..93f6025c71d 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -74,7 +74,6 @@ fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; cx.tcx - .sess .crate_types() .iter() .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro)) diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index e2a7ba02a04..7b0f7eaf1f0 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -122,7 +122,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { } else { return; }; - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind; + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind; if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind; if expr.span.ctxt() == inner_expr.span.ctxt(); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 8009b00b4b9..bf031ac8454 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -278,7 +278,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B. - const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [ + const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 13] = [ (&paths::SLICE_FROM_RAW_PARTS, &[0]), (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]), (&paths::PTR_COPY, &[0, 1]), @@ -291,20 +291,33 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]), (&paths::PTR_SWAP, &[0, 1]), (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]), - (&paths::PTR_WRITE, &[0]), - (&paths::PTR_WRITE_UNALIGNED, &[0]), - (&paths::PTR_WRITE_VOLATILE, &[0]), (&paths::PTR_WRITE_BYTES, &[0]), ]; + let invalid_null_ptr_usage_table_diag_items: [(Option<DefId>, &[usize]); 3] = [ + (cx.tcx.get_diagnostic_item(sym::ptr_write), &[0]), + (cx.tcx.get_diagnostic_item(sym::ptr_write_unaligned), &[0]), + (cx.tcx.get_diagnostic_item(sym::ptr_write_volatile), &[0]), + ]; if_chain! { if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>(); - if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE + if let Some(arg_indices) = INVALID_NULL_PTR_USAGE_TABLE .iter() - .find(|&&(fn_path, _)| fn_path == fun_def_path); + .find_map(|&(fn_path, indices)| if fn_path == fun_def_path { Some(indices) } else { None }) + .or_else(|| { + invalid_null_ptr_usage_table_diag_items + .iter() + .find_map(|&(def_id, indices)| { + if def_id == Some(fun_def_id) { + Some(indices) + } else { + None + } + }) + }); then { for &arg_idx in arg_indices { if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) { diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs index ff66b8a0095..d0de33e3c4f 100644 --- a/clippy_lints/src/question_mark_used.rs +++ b/clippy_lints/src/question_mark_used.rs @@ -34,7 +34,7 @@ declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind { + if let ExprKind::Match(_, _, MatchSource::TryDesugar(_)) = expr.kind { if !span_is_local(expr.span) { return; } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 4e3efe97b8c..fc49b58e0a7 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -52,7 +52,7 @@ impl ReturnVisitor { impl<'tcx> Visitor<'tcx> for ReturnVisitor { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) = ex.kind { + if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind { self.found_return = true; } else { hir_visit::walk_expr(self, ex); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 351bacf5691..d6b9a49d2fe 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -164,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { if !in_external_macro(cx.sess(), stmt.span) && let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Ret(Some(ret)) = expr.kind - && let ExprKind::Match(.., MatchSource::TryDesugar) = ret.kind + && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) && let ItemKind::Fn(_, _, body) = item.kind diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index dd120599c04..462b1aa8153 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) { !matches!( &arg.kind, - ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) + ExprKind::Match(.., MatchSource::TryDesugar(_)) | ExprKind::Path(..) ) } else { false diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 64dfe51b059..5ac4f0aa46c 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } match e.kind { - ExprKind::Match(_, arms, MatchSource::TryDesugar) => { + ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) => { let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else { return; }; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 3926b954ec8..1596bb77397 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.73" +version = "0.1.74" edition = "2021" publish = false diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 60fab1ec41a..6be8b8bb916 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -149,7 +149,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) { (Pat::Str("for"), Pat::Str("}")) }, ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")), - ExprKind::Match(e, _, MatchSource::TryDesugar) => (expr_search_pat(tcx, e).0, Pat::Str("?")), + ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => (expr_search_pat(tcx, e).0, Pat::Str("?")), ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => { (expr_search_pat(tcx, e).0, Pat::Str("await")) }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index fdc35cd4ddf..52214e733f1 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -5,6 +5,7 @@ use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; +use rustc_hir::MatchSource::TryDesugar; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, @@ -311,7 +312,7 @@ impl HirEqInterExpr<'_, '_, '_> { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { - ls == rs + (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_))))) && self.eq_expr(le, re) && over(la, ra, |l, r| { self.eq_pat(l.pat, r.pat) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 171b7faf219..d53cafde45d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -5,6 +5,7 @@ #![feature(lint_reasons)] #![feature(never_type)] #![feature(rustc_private)] +#![feature(assert_matches)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -83,7 +84,7 @@ use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; @@ -1765,7 +1766,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc if let ExprKind::Match(_, arms, ref source) = expr.kind { // desugared from a `?` operator - if *source == MatchSource::TryDesugar { + if let MatchSource::TryDesugar(_) = *source { return Some(expr); } @@ -2370,11 +2371,11 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new(); +static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new(); -fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); - let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap(); + let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap(); let value = map.entry(module); match value { Entry::Occupied(entry) => f(entry.get()), diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e72d063cfd9..a567c5cbdc9 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -87,10 +87,7 @@ pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"]; pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"]; -pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; -pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; -pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 4c695cb9b6e..43523faa236 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -291,8 +291,8 @@ fn check_terminator<'tcx>( | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } | TerminatorKind::Return - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate | TerminatorKind::Unreachable => Ok(()), TerminatorKind::Drop { place, .. } => { if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) { @@ -415,7 +415,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> if !matches!( impl_src, - ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) + ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_) ) { return false; } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a05f682aa8c..f0b4ede35fb 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::assert_matches::debug_assert_matches; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>( })), ); - debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait); + debug_assert_matches!( + tcx.def_kind(trait_id), + DefKind::Trait | DefKind::TraitAlias, + "`DefId` must belong to a trait or trait alias" + ); #[cfg(debug_assertions)] assert_generic_args_match(tcx, trait_id, trait_ref.args); diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index f83988a6e32..3b47a451345 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -161,7 +161,7 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { for_each_expr(expr, |e| { - if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) { + if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 3633ed31d73..1470da61fac 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.73" +version = "0.1.74" edition = "2021" publish = false diff --git a/rust-toolchain b/rust-toolchain index 8b3f819f0cd..19c09b58b98 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-08-10" +channel = "nightly-2023-08-24" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui/crashes/ice-11337.rs b/tests/ui/crashes/ice-11337.rs new file mode 100644 index 00000000000..0bed4035f6b --- /dev/null +++ b/tests/ui/crashes/ice-11337.rs @@ -0,0 +1,9 @@ +#![feature(trait_alias)] + +trait Confusing<F> = Fn(i32) where F: Fn(u32); + +fn alias<T: Confusing<F>, F>(_: T, _: F) {} + +fn main() { + alias(|_| {}, |_| {}); +} diff --git a/tests/ui/implied_bounds_in_impls.fixed b/tests/ui/implied_bounds_in_impls.fixed new file mode 100644 index 00000000000..952c2b70619 --- /dev/null +++ b/tests/ui/implied_bounds_in_impls.fixed @@ -0,0 +1,68 @@ +#![warn(clippy::implied_bounds_in_impls)] +#![allow(dead_code)] +#![feature(return_position_impl_trait_in_trait)] + +use std::ops::{Deref, DerefMut}; + +// Only one bound, nothing to lint. +fn normal_deref<T>(x: T) -> impl Deref<Target = T> { + Box::new(x) +} + +// Deref implied by DerefMut +fn deref_derefmut<T>(x: T) -> impl DerefMut<Target = T> { + Box::new(x) +} + +trait GenericTrait<T> {} +trait GenericTrait2<V> {} +// U is intentionally at a different "index" in GenericSubtrait than `T` is in GenericTrait, +// so this can be a good test to make sure that the calculations are right (no off-by-one errors, +// ...) +trait GenericSubtrait<T, U, V>: GenericTrait<U> + GenericTrait2<V> {} + +impl GenericTrait<i32> for () {} +impl GenericTrait<i64> for () {} +impl<V> GenericTrait2<V> for () {} +impl<V> GenericSubtrait<(), i32, V> for () {} +impl<V> GenericSubtrait<(), i64, V> for () {} + +fn generics_implied<U, W>() -> impl GenericSubtrait<U, W, U> +where + (): GenericSubtrait<U, W, U>, +{ +} + +fn generics_implied_multi<V>() -> impl GenericSubtrait<(), i32, V> {} + +fn generics_implied_multi2<T, V>() -> impl GenericSubtrait<(), T, V> +where + (): GenericSubtrait<(), T, V> + GenericTrait<T>, +{ +} + +// i32 != i64, GenericSubtrait<_, i64, _> does not imply GenericTrait<i32>, don't lint +fn generics_different() -> impl GenericTrait<i32> + GenericSubtrait<(), i64, ()> {} + +// i32 == i32, GenericSubtrait<_, i32, _> does imply GenericTrait<i32>, lint +fn generics_same() -> impl GenericSubtrait<(), i32, ()> {} + +trait SomeTrait { + // Check that it works in trait declarations. + fn f() -> impl DerefMut<Target = u8>; +} +struct SomeStruct; +impl SomeStruct { + // Check that it works in inherent impl blocks. + fn f() -> impl DerefMut<Target = u8> { + Box::new(123) + } +} +impl SomeTrait for SomeStruct { + // Check that it works in trait impls. + fn f() -> impl DerefMut<Target = u8> { + Box::new(42) + } +} + +fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.rs b/tests/ui/implied_bounds_in_impls.rs new file mode 100644 index 00000000000..475f2621bbf --- /dev/null +++ b/tests/ui/implied_bounds_in_impls.rs @@ -0,0 +1,68 @@ +#![warn(clippy::implied_bounds_in_impls)] +#![allow(dead_code)] +#![feature(return_position_impl_trait_in_trait)] + +use std::ops::{Deref, DerefMut}; + +// Only one bound, nothing to lint. +fn normal_deref<T>(x: T) -> impl Deref<Target = T> { + Box::new(x) +} + +// Deref implied by DerefMut +fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> { + Box::new(x) +} + +trait GenericTrait<T> {} +trait GenericTrait2<V> {} +// U is intentionally at a different "index" in GenericSubtrait than `T` is in GenericTrait, +// so this can be a good test to make sure that the calculations are right (no off-by-one errors, +// ...) +trait GenericSubtrait<T, U, V>: GenericTrait<U> + GenericTrait2<V> {} + +impl GenericTrait<i32> for () {} +impl GenericTrait<i64> for () {} +impl<V> GenericTrait2<V> for () {} +impl<V> GenericSubtrait<(), i32, V> for () {} +impl<V> GenericSubtrait<(), i64, V> for () {} + +fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U> +where + (): GenericSubtrait<U, W, U>, +{ +} + +fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {} + +fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V> +where + (): GenericSubtrait<(), T, V> + GenericTrait<T>, +{ +} + +// i32 != i64, GenericSubtrait<_, i64, _> does not imply GenericTrait<i32>, don't lint +fn generics_different() -> impl GenericTrait<i32> + GenericSubtrait<(), i64, ()> {} + +// i32 == i32, GenericSubtrait<_, i32, _> does imply GenericTrait<i32>, lint +fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {} + +trait SomeTrait { + // Check that it works in trait declarations. + fn f() -> impl Deref + DerefMut<Target = u8>; +} +struct SomeStruct; +impl SomeStruct { + // Check that it works in inherent impl blocks. + fn f() -> impl Deref + DerefMut<Target = u8> { + Box::new(123) + } +} +impl SomeTrait for SomeStruct { + // Check that it works in trait impls. + fn f() -> impl Deref + DerefMut<Target = u8> { + Box::new(42) + } +} + +fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.stderr b/tests/ui/implied_bounds_in_impls.stderr new file mode 100644 index 00000000000..8dffc674444 --- /dev/null +++ b/tests/ui/implied_bounds_in_impls.stderr @@ -0,0 +1,123 @@ +error: this bound is already specified as the supertrait of `DerefMut<Target = T>` + --> $DIR/implied_bounds_in_impls.rs:13:36 + | +LL | fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> { + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` +help: try removing this bound + | +LL - fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> { +LL + fn deref_derefmut<T>(x: T) -> impl DerefMut<Target = T> { + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<U, W, U>` + --> $DIR/implied_bounds_in_impls.rs:30:37 + | +LL | fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U> + | ^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U> +LL + fn generics_implied<U, W>() -> impl GenericSubtrait<U, W, U> + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>` + --> $DIR/implied_bounds_in_impls.rs:36:40 + | +LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {} + | ^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {} +LL + fn generics_implied_multi<V>() -> impl GenericTrait2<V> + GenericSubtrait<(), i32, V> {} + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>` + --> $DIR/implied_bounds_in_impls.rs:36:60 + | +LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {} + | ^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {} +LL + fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, V> {} + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>` + --> $DIR/implied_bounds_in_impls.rs:38:44 + | +LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V> + | ^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V> +LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait2<V> + GenericSubtrait<(), T, V> + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>` + --> $DIR/implied_bounds_in_impls.rs:38:62 + | +LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V> + | ^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V> +LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericSubtrait<(), T, V> + | + +error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, ()>` + --> $DIR/implied_bounds_in_impls.rs:48:28 + | +LL | fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {} + | ^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {} +LL + fn generics_same() -> impl GenericSubtrait<(), i32, ()> {} + | + +error: this bound is already specified as the supertrait of `DerefMut<Target = u8>` + --> $DIR/implied_bounds_in_impls.rs:52:20 + | +LL | fn f() -> impl Deref + DerefMut<Target = u8>; + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Deref + DerefMut<Target = u8>; +LL + fn f() -> impl DerefMut<Target = u8>; + | + +error: this bound is already specified as the supertrait of `DerefMut<Target = u8>` + --> $DIR/implied_bounds_in_impls.rs:57:20 + | +LL | fn f() -> impl Deref + DerefMut<Target = u8> { + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Deref + DerefMut<Target = u8> { +LL + fn f() -> impl DerefMut<Target = u8> { + | + +error: this bound is already specified as the supertrait of `DerefMut<Target = u8>` + --> $DIR/implied_bounds_in_impls.rs:63:20 + | +LL | fn f() -> impl Deref + DerefMut<Target = u8> { + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Deref + DerefMut<Target = u8> { +LL + fn f() -> impl DerefMut<Target = u8> { + | + +error: aborting due to 10 previous errors + diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 9dd046fec1f..7d8a584b022 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -63,11 +63,9 @@ fn main() { let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet - let _ = vec.iter().cloned().all(|x| x.len() == 1); + let _ = vec.iter().all(|x| x.len() == 1); - // Not implemented yet - let _ = vec.iter().cloned().any(|x| x.len() == 1); + let _ = vec.iter().any(|x| x.len() == 1); // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 3cab3669554..58c374ab8cd 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -64,10 +64,8 @@ fn main() { let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet let _ = vec.iter().cloned().all(|x| x.len() == 1); - // Not implemented yet let _ = vec.iter().cloned().any(|x| x.len() == 1); // Should probably stay as it is. diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 4417a40a63b..1f4c9040f77 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -146,5 +146,21 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | | | help: try: `.for_each(|x| assert!(!x.is_empty()))` -error: aborting due to 17 previous errors +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:67:13 + | +LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.all(|x| x.len() == 1)` + +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:69:13 + | +LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.any(|x| x.len() == 1)` + +error: aborting due to 19 previous errors |
