From cf0b55eb6232491a7c29ac0f47564b6ee7adb13e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 11 Apr 2023 19:47:44 +0000 Subject: [PATCH] Add `needless_maybe_sized` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/needless_maybe_sized.rs | 164 ++++++++ .../ui-toml/type_repetition_in_bounds/main.rs | 1 + .../type_repetition_in_bounds/main.stderr | 2 +- tests/ui/auxiliary/proc_macros.rs | 6 +- tests/ui/needless_maybe_sized.fixed | 116 ++++++ tests/ui/needless_maybe_sized.rs | 119 ++++++ tests/ui/needless_maybe_sized.stderr | 353 ++++++++++++++++++ tests/ui/type_repetition_in_bounds.rs | 6 +- tests/ui/type_repetition_in_bounds.stderr | 10 +- 12 files changed, 771 insertions(+), 10 deletions(-) create mode 100644 clippy_lints/src/needless_maybe_sized.rs create mode 100644 tests/ui/needless_maybe_sized.fixed create mode 100644 tests/ui/needless_maybe_sized.rs create mode 100644 tests/ui/needless_maybe_sized.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index bd3a04e34ae..aaa53d8a6cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5523,6 +5523,7 @@ Released 2018-09-13 [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match +[`needless_maybe_sized`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_maybe_sized [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5ff7d8e5134..dd16e13e7ad 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -527,6 +527,7 @@ crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_if::NEEDLESS_IF_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e2aac58bf97..bfddbffe9b2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -254,6 +254,7 @@ mod needless_for_each; mod needless_if; mod needless_late_init; +mod needless_maybe_sized; mod needless_parens_on_range_literals; mod needless_pass_by_ref_mut; mod needless_pass_by_value; @@ -1032,6 +1033,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static 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(|_| Box::new(allow_attributes::AllowAttribute)); diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs new file mode 100644 index 00000000000..06ae1723a03 --- /dev/null +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{ClauseKind, PredicatePolarity}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// ### What it does + /// Lints `?Sized` bounds applied to type parameters that cannot be unsized + /// + /// ### Why is this bad? + /// The `?Sized` bound is misleading because it cannot be satisfied by an + /// unsized type + /// + /// ### Example + /// ```rust + /// // `T` cannot be unsized because `Clone` requires it to be `Sized` + /// fn f(t: &T) {} + /// ``` + /// Use instead: + /// ```rust + /// fn f(t: &T) {} + /// + /// // or choose alternative bounds for `T` so that it can be unsized + /// ``` + #[clippy::version = "1.79.0"] + pub NEEDLESS_MAYBE_SIZED, + suspicious, + "a `?Sized` bound that is unusable due to a `Sized` requirement" +} +declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); + +#[allow(clippy::struct_field_names)] +struct Bound<'tcx> { + /// The [`DefId`] of the type parameter the bound refers to + param: DefId, + ident: Ident, + + trait_bound: &'tcx PolyTraitRef<'tcx>, + modifier: TraitBoundModifier, + + predicate_pos: usize, + bound_pos: usize, +} + +/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion +fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator> { + generics + .predicates + .iter() + .enumerate() + .filter_map(|(predicate_pos, predicate)| { + let WherePredicate::BoundPredicate(bound_predicate) = predicate else { + return None; + }; + + let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?; + + Some( + bound_predicate + .bounds + .iter() + .enumerate() + .filter_map(move |(bound_pos, bound)| match bound { + &GenericBound::Trait(ref trait_bound, modifier) => Some(Bound { + param, + ident, + trait_bound, + modifier, + predicate_pos, + bound_pos, + }), + GenericBound::Outlives(_) => None, + }) + .filter(|bound| !bound.trait_bound.span.from_expansion()), + ) + }) + .flatten() +} + +/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the +/// path taken to find a `Sized` bound if one is found +fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option> { + fn search(cx: &LateContext<'_>, path: &mut Vec) -> bool { + let trait_def_id = *path.last().unwrap(); + + if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() { + return true; + } + + for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.polarity == PredicatePolarity::Positive + && !path.contains(&trait_predicate.def_id()) + { + path.push(trait_predicate.def_id()); + if search(cx, path) { + return true; + } + path.pop(); + } + } + + false + } + + let mut path = vec![trait_bound.trait_ref.trait_def_id()?]; + search(cx, &mut path).then_some(path) +} + +impl LateLintPass<'_> for NeedlessMaybeSized { + fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) { + let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { + return; + }; + + let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics) + .filter(|bound| { + bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait) + && bound.modifier == TraitBoundModifier::Maybe + }) + .map(|bound| (bound.param, bound)) + .collect(); + + for bound in type_param_bounds(generics) { + if bound.modifier == TraitBoundModifier::None + && let Some(sized_bound) = maybe_sized_params.get(&bound.param) + && let Some(path) = path_to_sized_bound(cx, bound.trait_bound) + { + span_lint_and_then( + cx, + NEEDLESS_MAYBE_SIZED, + sized_bound.trait_bound.span, + "`?Sized` bound is ignored because of a `Sized` requirement", + |diag| { + let ty_param = sized_bound.ident; + diag.span_note( + bound.trait_bound.span, + format!("`{ty_param}` cannot be unsized because of the bound"), + ); + + for &[current_id, next_id] in path.array_windows() { + let current = cx.tcx.item_name(current_id); + let next = cx.tcx.item_name(next_id); + diag.note(format!("...because `{current}` has the bound `{next}`")); + } + + diag.span_suggestion_verbose( + generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos), + "change the bounds that require `Sized`, or remove the `?Sized` bound", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + + return; + } + } + } +} diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs index 2454c10382d..7f93d2071c9 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_maybe_sized)] #![warn(clippy::type_repetition_in_bounds)] fn f() diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr index 6005f76b94b..c5102c39d1c 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui-toml/type_repetition_in_bounds/main.rs:13:5 + --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 3303eb14567..4a81a191e72 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -57,7 +57,7 @@ fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Gro const ESCAPE_CHAR: char = '$'; /// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// span set to that of the first token. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn with_span(input: TokenStream) -> TokenStream { let mut iter = input.into_iter(); @@ -71,7 +71,7 @@ pub fn with_span(input: TokenStream) -> TokenStream { } /// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// from an external macro. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn external(input: TokenStream) -> TokenStream { let mut res = TokenStream::new(); @@ -83,7 +83,7 @@ pub fn external(input: TokenStream) -> TokenStream { } /// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. +/// either by `$ident` or `$(tokens)`. fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { while let Some(tt) = input.next() { match tt { diff --git a/tests/ui/needless_maybe_sized.fixed b/tests/ui/needless_maybe_sized.fixed new file mode 100644 index 00000000000..4d24a7cee61 --- /dev/null +++ b/tests/ui/needless_maybe_sized.fixed @@ -0,0 +1,116 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1(t: &T) {} +fn depth_2(t: &T) {} + +// We only need to show one +fn multiple_paths(t: &T) {} + +fn in_where(t: &T) +where + T: Sized, +{ +} + +fn mixed_1(t: &T) +{ +} + +fn mixed_2(t: &T) +where + T: Sized, +{ +} + +fn mixed_3(t: &T) +where + T: Sized, +{ +} + +struct Struct(T); + +impl Struct { + fn method(&self) {} +} + +enum Enum { + Variant(&'static T), +} + +union Union<'a, T: Sized> { + a: &'a T, +} + +trait Trait { + fn trait_method() {} + + type GAT; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait() {} + +fn impl_trait(_: &(impl Sized)) {} + +trait GenericTrait: Sized {} +fn in_generic_trait, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph() {} +} + +// Should not lint + +fn sized() {} +fn maybe_sized() {} + +struct SeparateBounds(T); +impl SeparateBounds {} + +trait P {} +trait Q: P {} + +fn ok_depth_1() {} +fn ok_depth_2() {} + +external! { + fn in_macro(t: &T) {} + + fn with_local_clone(t: &T) {} +} + +#[derive(Clone)] +struct InDerive { + t: T, +} + +struct Refined(T); +impl Refined {} + +fn main() {} diff --git a/tests/ui/needless_maybe_sized.rs b/tests/ui/needless_maybe_sized.rs new file mode 100644 index 00000000000..ef66f9a4f2a --- /dev/null +++ b/tests/ui/needless_maybe_sized.rs @@ -0,0 +1,119 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1(t: &T) {} +fn depth_2(t: &T) {} + +// We only need to show one +fn multiple_paths(t: &T) {} + +fn in_where(t: &T) +where + T: Sized + ?Sized, +{ +} + +fn mixed_1(t: &T) +where + T: ?Sized, +{ +} + +fn mixed_2(t: &T) +where + T: Sized, +{ +} + +fn mixed_3(t: &T) +where + T: Sized, + T: ?Sized, +{ +} + +struct Struct(T); + +impl Struct { + fn method(&self) {} +} + +enum Enum { + Variant(&'static T), +} + +union Union<'a, T: Sized + ?Sized> { + a: &'a T, +} + +trait Trait { + fn trait_method() {} + + type GAT; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait() {} + +fn impl_trait(_: &(impl Sized + ?Sized)) {} + +trait GenericTrait: Sized {} +fn in_generic_trait + ?Sized, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph() {} +} + +// Should not lint + +fn sized() {} +fn maybe_sized() {} + +struct SeparateBounds(T); +impl SeparateBounds {} + +trait P {} +trait Q: P {} + +fn ok_depth_1() {} +fn ok_depth_2() {} + +external! { + fn in_macro(t: &T) {} + + fn with_local_clone(t: &T) {} +} + +#[derive(Clone)] +struct InDerive { + t: T, +} + +struct Refined(T); +impl Refined {} + +fn main() {} diff --git a/tests/ui/needless_maybe_sized.stderr b/tests/ui/needless_maybe_sized.stderr new file mode 100644 index 00000000000..3b1d2b49b06 --- /dev/null +++ b/tests/ui/needless_maybe_sized.stderr @@ -0,0 +1,353 @@ +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:9:24 + | +LL | fn directly(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:9:16 + | +LL | fn directly(t: &T) {} + | ^^^^^ + = note: `-D clippy::needless-maybe-sized` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_maybe_sized)]` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn directly(t: &T) {} +LL + fn directly(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:14:19 + | +LL | fn depth_1(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:14:15 + | +LL | fn depth_1(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_1(t: &T) {} +LL + fn depth_1(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:15:19 + | +LL | fn depth_2(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:15:15 + | +LL | fn depth_2(t: &T) {} + | ^ + = note: ...because `B` has the bound `A` + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_2(t: &T) {} +LL + fn depth_2(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:18:30 + | +LL | fn multiple_paths(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:18:22 + | +LL | fn multiple_paths(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn multiple_paths(t: &T) {} +LL + fn multiple_paths(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:22:16 + | +LL | T: Sized + ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:22:8 + | +LL | T: Sized + ?Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized + ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:28:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:26:15 + | +LL | fn mixed_1(t: &T) + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - where +LL - T: ?Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:32:15 + | +LL | fn mixed_2(t: &T) + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:34:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn mixed_2(t: &T) +LL + fn mixed_2(t: &T) + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:41:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:40:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized, +LL - T: ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:45:26 + | +LL | struct Struct(T); + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:45:18 + | +LL | struct Struct(T); + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - struct Struct(T); +LL + struct Struct(T); + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:47:17 + | +LL | impl Struct { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:47:9 + | +LL | impl Struct { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - impl Struct { +LL + impl Struct { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:48:26 + | +LL | fn method(&self) {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:48:18 + | +LL | fn method(&self) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn method(&self) {} +LL + fn method(&self) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:51:22 + | +LL | enum Enum { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:51:14 + | +LL | enum Enum { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - enum Enum { +LL + enum Enum { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:55:28 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:55:20 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - union Union<'a, T: Sized + ?Sized> { +LL + union Union<'a, T: Sized> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:59:24 + | +LL | trait Trait { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:59:16 + | +LL | trait Trait { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - trait Trait { +LL + trait Trait { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:60:32 + | +LL | fn trait_method() {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:60:24 + | +LL | fn trait_method() {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn trait_method() {} +LL + fn trait_method() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:62:25 + | +LL | type GAT; + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:62:17 + | +LL | type GAT; + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - type GAT; +LL + type GAT; + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:68:23 + | +LL | fn second_in_trait() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:68:32 + | +LL | fn second_in_trait() {} + | ^^^^^^^^^^^^^ + = note: ...because `SecondInTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn second_in_trait() {} +LL + fn second_in_trait() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:70:33 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^^ + | +note: `impl Sized + ?Sized` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:70:25 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn impl_trait(_: &(impl Sized + ?Sized)) {} +LL + fn impl_trait(_: &(impl Sized)) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:73:42 + | +LL | fn in_generic_trait + ?Sized, U>() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:73:24 + | +LL | fn in_generic_trait + ?Sized, U>() {} + | ^^^^^^^^^^^^^^^ + = note: ...because `GenericTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn in_generic_trait + ?Sized, U>() {} +LL + fn in_generic_trait, U>() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:88:29 + | +LL | fn larger_graph() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:88:24 + | +LL | fn larger_graph() {} + | ^^ + = note: ...because `A1` has the bound `B2` + = note: ...because `B2` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn larger_graph() {} +LL + fn larger_graph() {} + | + +error: aborting due to 20 previous errors + diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 0039c805b7d..d325887bfba 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -1,5 +1,9 @@ #![deny(clippy::type_repetition_in_bounds)] -#![allow(clippy::extra_unused_type_parameters, clippy::multiple_bound_locations)] +#![allow( + clippy::extra_unused_type_parameters, + clippy::multiple_bound_locations, + clippy::needless_maybe_sized +)] use serde::Deserialize; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index e9c6b41aaa8..77944c95045 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:10:5 + --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:28:5 + --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:103:5 + --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:109:5 + --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | T: ?Sized, = help: consider combining the bounds: `T: Clone + ?Sized` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:135:9 + --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^