Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mod passes;
mod precedence;
mod ptr_nulls;
mod redundant_semicolon;
mod redundant_sizedness_bounds;
mod reference_casting;
mod shadowed_into_iter;
mod static_mut_refs;
Expand Down Expand Up @@ -111,6 +112,7 @@ use pass_by_value::*;
use precedence::*;
use ptr_nulls::*;
use redundant_semicolon::*;
use redundant_sizedness_bounds::RedundantSizednessBounds;
use reference_casting::*;
use rustc_hir::def_id::LocalModDefId;
use rustc_middle::query::Providers;
Expand Down Expand Up @@ -246,6 +248,7 @@ late_lint_methods!(
UnqualifiedLocalImports: UnqualifiedLocalImports,
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
RedundantSizednessBounds: RedundantSizednessBounds,
]
]
);
Expand Down
241 changes: 241 additions & 0 deletions compiler/rustc_lint/src/redundant_sizedness_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
use rustc_errors::Applicability;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_hir::{
BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind,
};
use rustc_middle::ty::{ClauseKind, PredicatePolarity};
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::symbol::Ident;

use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `redundant_sizedness_bounds` lint detects redundant sizedness bounds
/// applied to type parameters that are already otherwise implied.
///
/// ### Example
///
/// ```rust
/// // `T` must be `Sized` due to the bound `Clone`, thus `?Sized` is redundant.
/// fn f<T: Clone + ?Sized>(t: &T) {}
/// // `T` is `Sized` due to `Default` bound, thus the explicit `Sized` bound is redundant.
/// fn g<T: Default + Sized>(t: &T) {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Sizedness bounds that have no effect, as another bound implies `Sized`,
/// are redundant and can be misleading. This lint notifies the user of
/// these redundant bounds.
pub REDUNDANT_SIZEDNESS_BOUNDS,
Warn,
"a sizedness bound that is redundant due to another bound"
}
declare_lint_pass!(RedundantSizednessBounds => [REDUNDANT_SIZEDNESS_BOUNDS]);

struct Bound<'tcx> {
/// The [`DefId`] of the type parameter the bound refers to.
param: DefId,
/// Identifier of type parameter.
ident: Ident,
/// A reference to the trait bound applied to the parameter.
trait_bound: &'tcx PolyTraitRef<'tcx>,
/// The index of the predicate within the generics predicate list.
predicate_pos: usize,
/// Position of the bound in the bounds list of a predicate.
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<Item = Bound<'tcx>> {
generics
.predicates
.iter()
.enumerate()
.filter_map(|(predicate_pos, predicate)| {
let WherePredicateKind::BoundPredicate(bound_predicate) = &predicate.kind 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(trait_bound) => {
Some(Bound { param, ident, trait_bound, predicate_pos, bound_pos })
}
GenericBound::Outlives(_) | GenericBound::Use(..) => 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 the `target` bound if one is found.
fn path_to_bound(
cx: &LateContext<'_>,
trait_bound: &PolyTraitRef<'_>,
target: DefId,
) -> Option<Vec<DefId>> {
fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>, target: DefId) -> bool {
let trait_def_id = *path.last().unwrap();

if trait_def_id == target {
return true;
}

for (predicate, _) in
cx.tcx.explicit_super_predicates_of(trait_def_id).iter_identity_copied()
{
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, target) {
return true;
}
path.pop();
}
}

false
}

let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
search(cx, &mut path, target).then_some(path)
}

// Checks if there exists a bound `redundant_bound` that is already implied by `implicit_bound`.
fn check_redundant_sizedness_bounds(
redundant_bound: DefId,
redundant_bound_polarity: BoundPolarity,
implicit_bound: DefId,
cx: &LateContext<'_>,
generics: &Generics<'_>,
) -> bool {
let redundant_sized_params: DefIdMap<_> = type_param_bounds(generics)
.filter(|bound| {
bound.trait_bound.trait_ref.trait_def_id() == Some(redundant_bound)
// Here we wish to compare the variant of the enum `BoundPolarity` whilst
// disregarding the contents of the variant.
&& std::mem::discriminant(&bound.trait_bound.modifiers.polarity)
== std::mem::discriminant(&redundant_bound_polarity)
})
.map(|bound| (bound.param, bound))
.collect();

for bound in type_param_bounds(generics) {
if bound.trait_bound.modifiers == TraitBoundModifiers::NONE
&& let Some(redundant_sized_bound) = redundant_sized_params.get(&bound.param)
&& let Some(path) = path_to_bound(cx, bound.trait_bound, implicit_bound)
{
let redundant_bound_polarity_str = match redundant_bound_polarity {
BoundPolarity::Maybe(_) => "?",
_ => "",
};
cx.span_lint(
REDUNDANT_SIZEDNESS_BOUNDS,
redundant_sized_bound.trait_bound.span,
|diag| {
let redundant_bound_str = cx.tcx.def_path_str(redundant_bound);
let implicit_bound_str = cx.tcx.def_path_str(implicit_bound);

diag.primary_message(format!(
"`{}{}` bound is redundant because of a `{}` requirement",
redundant_bound_polarity_str, redundant_bound_str, implicit_bound_str,
));
let ty_param = redundant_sized_bound.ident;
diag.span_note(
bound.trait_bound.span,
format!(
"`{ty_param}` is implied to be `{}` because of the bound",
implicit_bound_str,
),
);

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(
redundant_sized_bound.predicate_pos,
redundant_sized_bound.bound_pos,
),
format!(
"change the bounds that require `{}`, or remove the `{}{}` bound",
implicit_bound_str, redundant_bound_polarity_str, redundant_bound_str,
),
"",
Applicability::MaybeIncorrect,
);
},
);

return true;
}
}
false
}

impl LateLintPass<'_> for RedundantSizednessBounds {
fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
return;
};
let Some(meta_sized_trait) = cx.tcx.lang_items().meta_sized_trait() else {
return;
};
let Some(pointee_sized_trait) = cx.tcx.lang_items().pointee_sized_trait() else {
return;
};

if check_redundant_sizedness_bounds(
sized_trait,
BoundPolarity::Maybe(Default::default()),
sized_trait,
cx,
generics,
) {
return;
}
if check_redundant_sizedness_bounds(
meta_sized_trait,
BoundPolarity::Positive,
sized_trait,
cx,
generics,
) {
return;
}
if check_redundant_sizedness_bounds(
pointee_sized_trait,
BoundPolarity::Positive,
sized_trait,
cx,
generics,
) {
return;
}
if check_redundant_sizedness_bounds(
pointee_sized_trait,
BoundPolarity::Positive,
meta_sized_trait,
cx,
generics,
) {
return;
}
}
}
6 changes: 3 additions & 3 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

#![stable(feature = "rust1", since = "1.0.0")]

use crate::marker::{Destruct, PointeeSized};
use crate::marker::Destruct;

mod uninit;

Expand Down Expand Up @@ -322,7 +322,7 @@ impl_use_cloned! {
reason = "deriving hack, should not be public",
issue = "none"
)]
pub struct AssertParamIsClone<T: Clone + PointeeSized> {
pub struct AssertParamIsClone<T: Clone> {
_field: crate::marker::PhantomData<T>,
}
#[doc(hidden)]
Expand All @@ -332,7 +332,7 @@ pub struct AssertParamIsClone<T: Clone + PointeeSized> {
reason = "deriving hack, should not be public",
issue = "none"
)]
pub struct AssertParamIsCopy<T: Copy + PointeeSized> {
pub struct AssertParamIsCopy<T: Copy> {
_field: crate::marker::PhantomData<T>,
}

Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sync/nonpoison/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ impl<T> From<T> for Mutex<T> {
}

#[unstable(feature = "nonpoison_mutex", issue = "134645")]
impl<T: ?Sized + Default> Default for Mutex<T> {
impl<T: Default> Default for Mutex<T> {
/// Creates a `Mutex<T>`, with the `Default` value for T.
fn default() -> Mutex<T> {
Mutex::new(Default::default())
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sync/poison/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ impl<T> From<T> for Mutex<T> {
}

#[stable(feature = "mutex_default", since = "1.10.0")]
impl<T: ?Sized + Default> Default for Mutex<T> {
impl<T: Default> Default for Mutex<T> {
/// Creates a `Mutex<T>`, with the `Default` value for T.
fn default() -> Mutex<T> {
Mutex::new(Default::default())
Expand Down
1 change: 0 additions & 1 deletion src/tools/clippy/clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
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,
Expand Down
2 changes: 2 additions & 0 deletions src/tools/clippy/clippy_lints/src/deprecated_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [
("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
#[clippy::version = "1.80.0"]
("clippy::mismatched_target_os", "unexpected_cfgs"),
#[clippy::version = "1.91.0"]
("clippy::needless_maybe_sized", "redundant_sizedness_bounds"),
#[clippy::version = ""]
("clippy::new_without_default_derive", "clippy::new_without_default"),
#[clippy::version = ""]
Expand Down
2 changes: 0 additions & 2 deletions src/tools/clippy/clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ mod needless_else;
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;
Expand Down Expand Up @@ -740,7 +739,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
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_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
Expand Down
Loading
Loading