Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
239 changes: 239 additions & 0 deletions compiler/rustc_lint/src/redundant_sizedness_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
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)
&& 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