Skip to content

Commit 96670d0

Browse files
committed
Extends lint
- Extends lint to check for redundant sizedness bounds. - Applies the extended lint's suggestions to `clone.rs` as flagged by the lint.
1 parent d262cba commit 96670d0

File tree

3 files changed

+263
-125
lines changed

3 files changed

+263
-125
lines changed

compiler/rustc_lint/src/redundant_sizedness_bound.rs

Lines changed: 125 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,43 @@ use rustc_span::symbol::Ident;
1010
use crate::{LateContext, LateLintPass, LintContext};
1111

1212
declare_lint! {
13-
/// The `redundant_sizedness_bound` lint detects `?Sized` bounds applied to type parameters that cannot be unsized.
13+
/// The `redundant_sizedness_bound` lint detects redundant sizedness bounds applied to type parameters that are already
14+
/// otherwise implied.
1415
///
1516
/// ### Example
1617
///
1718
/// ```rust
19+
/// #![feature(sized_hierarchy)]
20+
/// use std::marker::MetaSized;
1821
/// // `T` cannot be unsized because `Clone` requires it to be `Sized`
1922
/// fn f<T: Clone + ?Sized>(t: &T) {}
23+
/// // `T` is `Sized` due to `Clone` bound, thereby implying `MetaSized` and making the explicit `MetaSized` bound redundant.
24+
/// fn g<T: MetaSized + Clone>(t: &T) {}
2025
/// ```
2126
///
2227
/// {{produces}}
2328
///
2429
/// ### Explanation
2530
///
2631
/// The `?Sized` bound is misleading because it cannot be satisfied by an
27-
/// unsized type. This lint notifies the user of said redundant bound.
32+
/// unsized type, similarly sizedness bounds that are already implied by other bounds.
33+
/// This lint notifies the user of said redundant bound.
2834
pub REDUNDANT_SIZEDNESS_BOUND,
2935
Warn,
30-
"a `?Sized` bound that is unusable due to a `Sized` requirement"
36+
"a sizedness bound that is redundant due to another bound"
3137
}
3238
declare_lint_pass!(RedundantSizednessBound => [REDUNDANT_SIZEDNESS_BOUND]);
3339

3440
struct Bound<'tcx> {
3541
/// The [`DefId`] of the type parameter the bound refers to
3642
param: DefId,
43+
/// Identifier of type parameter
3744
ident: Ident,
38-
45+
/// A reference to the trait bound applied to the parameter
3946
trait_bound: &'tcx PolyTraitRef<'tcx>,
40-
47+
/// The index of the predicate within the generics predicate list
4148
predicate_pos: usize,
49+
/// Position of the bound in the bounds list of a predicate
4250
bound_pos: usize,
4351
}
4452

@@ -73,12 +81,16 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
7381
}
7482

7583
/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the
76-
/// path taken to find a `Sized` bound if one is found
77-
fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option<Vec<DefId>> {
78-
fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>) -> bool {
84+
/// path taken to find the `target` bound if one is found
85+
fn path_to_bound(
86+
cx: &LateContext<'_>,
87+
trait_bound: &PolyTraitRef<'_>,
88+
target: DefId,
89+
) -> Option<Vec<DefId>> {
90+
fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>, target: DefId) -> bool {
7991
let trait_def_id = *path.last().unwrap();
8092

81-
if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() {
93+
if trait_def_id == target {
8294
return true;
8395
}
8496

@@ -90,7 +102,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
90102
&& !path.contains(&trait_predicate.def_id())
91103
{
92104
path.push(trait_predicate.def_id());
93-
if search(cx, path) {
105+
if search(cx, path, target) {
94106
return true;
95107
}
96108
path.pop();
@@ -101,36 +113,53 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
101113
}
102114

103115
let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
104-
search(cx, &mut path).then_some(path)
116+
search(cx, &mut path, target).then_some(path)
105117
}
106118

107-
impl LateLintPass<'_> for RedundantSizednessBound {
108-
fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
109-
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
110-
return;
111-
};
119+
// Checks if there exists a bound `redundant_bound` that is already implied by `implicit_bound`
120+
fn check_redundant_sizedness_bound(
121+
redundant_bound: DefId,
122+
redundant_bound_polarity: BoundPolarity,
123+
implicit_bound: DefId,
124+
cx: &LateContext<'_>,
125+
generics: &Generics<'_>,
126+
) -> bool {
127+
let redundant_sized_params: DefIdMap<_> = type_param_bounds(generics)
128+
.filter(|bound| {
129+
bound.trait_bound.trait_ref.trait_def_id() == Some(redundant_bound)
130+
&& std::mem::discriminant(&bound.trait_bound.modifiers.polarity)
131+
== std::mem::discriminant(&redundant_bound_polarity)
132+
})
133+
.map(|bound| (bound.param, bound))
134+
.collect();
112135

113-
let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
114-
.filter(|bound| {
115-
bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
116-
&& matches!(bound.trait_bound.modifiers.polarity, BoundPolarity::Maybe(_))
117-
})
118-
.map(|bound| (bound.param, bound))
119-
.collect();
120-
121-
for bound in type_param_bounds(generics) {
122-
if bound.trait_bound.modifiers == TraitBoundModifiers::NONE
123-
&& let Some(sized_bound) = maybe_sized_params.get(&bound.param)
124-
&& let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
125-
{
126-
cx.span_lint(REDUNDANT_SIZEDNESS_BOUND, sized_bound.trait_bound.span, |diag| {
127-
diag.primary_message(
128-
"`?Sized` bound is ignored because of a `Sized` requirement",
129-
);
130-
let ty_param = sized_bound.ident;
136+
for bound in type_param_bounds(generics) {
137+
if bound.trait_bound.modifiers == TraitBoundModifiers::NONE
138+
&& let Some(redundant_sized_bound) = redundant_sized_params.get(&bound.param)
139+
&& let Some(path) = path_to_bound(cx, bound.trait_bound, implicit_bound)
140+
{
141+
let redundant_bound_polarity_str = match redundant_bound_polarity {
142+
BoundPolarity::Maybe(_) => "?",
143+
_ => "",
144+
};
145+
cx.span_lint(
146+
REDUNDANT_SIZEDNESS_BOUND,
147+
redundant_sized_bound.trait_bound.span,
148+
|diag| {
149+
let redundant_bound_str = cx.tcx.def_path_str(redundant_bound);
150+
let implicit_bound_str = cx.tcx.def_path_str(implicit_bound);
151+
152+
diag.primary_message(format!(
153+
"`{}{}` bound is redundant because of a `{}` requirement",
154+
redundant_bound_polarity_str, redundant_bound_str, implicit_bound_str,
155+
));
156+
let ty_param = redundant_sized_bound.ident;
131157
diag.span_note(
132158
bound.trait_bound.span,
133-
format!("`{ty_param}` cannot be unsized because of the bound"),
159+
format!(
160+
"`{ty_param}` is implied to be `{}` because of the bound",
161+
implicit_bound_str,
162+
),
134163
);
135164

136165
for &[current_id, next_id] in path.array_windows() {
@@ -141,17 +170,72 @@ impl LateLintPass<'_> for RedundantSizednessBound {
141170

142171
diag.span_suggestion_verbose(
143172
generics.span_for_bound_removal(
144-
sized_bound.predicate_pos,
145-
sized_bound.bound_pos,
173+
redundant_sized_bound.predicate_pos,
174+
redundant_sized_bound.bound_pos,
175+
),
176+
format!(
177+
"change the bounds that require `{}`, or remove the `{}{}` bound",
178+
implicit_bound_str, redundant_bound_polarity_str, redundant_bound_str,
146179
),
147-
"change the bounds that require `Sized`, or remove the `?Sized` bound",
148180
"",
149181
Applicability::MaybeIncorrect,
150182
);
151-
});
183+
},
184+
);
152185

153-
return;
154-
}
186+
return true;
187+
}
188+
}
189+
false
190+
}
191+
192+
impl LateLintPass<'_> for RedundantSizednessBound {
193+
fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
194+
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
195+
return;
196+
};
197+
let Some(meta_sized_trait) = cx.tcx.lang_items().meta_sized_trait() else {
198+
return;
199+
};
200+
let Some(pointee_sized_trait) = cx.tcx.lang_items().pointee_sized_trait() else {
201+
return;
202+
};
203+
204+
if check_redundant_sizedness_bound(
205+
sized_trait,
206+
BoundPolarity::Maybe(Default::default()),
207+
sized_trait,
208+
cx,
209+
generics,
210+
) {
211+
return;
212+
}
213+
if check_redundant_sizedness_bound(
214+
meta_sized_trait,
215+
BoundPolarity::Positive,
216+
sized_trait,
217+
cx,
218+
generics,
219+
) {
220+
return;
221+
}
222+
if check_redundant_sizedness_bound(
223+
pointee_sized_trait,
224+
BoundPolarity::Positive,
225+
sized_trait,
226+
cx,
227+
generics,
228+
) {
229+
return;
230+
}
231+
if check_redundant_sizedness_bound(
232+
pointee_sized_trait,
233+
BoundPolarity::Positive,
234+
meta_sized_trait,
235+
cx,
236+
generics,
237+
) {
238+
return;
155239
}
156240
}
157241
}

library/core/src/clone.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
3737
#![stable(feature = "rust1", since = "1.0.0")]
3838

39-
use crate::marker::{Destruct, PointeeSized};
39+
use crate::marker::Destruct;
4040

4141
mod uninit;
4242

@@ -323,7 +323,7 @@ impl_use_cloned! {
323323
reason = "deriving hack, should not be public",
324324
issue = "none"
325325
)]
326-
pub struct AssertParamIsClone<T: Clone + PointeeSized> {
326+
pub struct AssertParamIsClone<T: Clone> {
327327
_field: crate::marker::PhantomData<T>,
328328
}
329329
#[doc(hidden)]
@@ -333,7 +333,7 @@ pub struct AssertParamIsClone<T: Clone + PointeeSized> {
333333
reason = "deriving hack, should not be public",
334334
issue = "none"
335335
)]
336-
pub struct AssertParamIsCopy<T: Copy + PointeeSized> {
336+
pub struct AssertParamIsCopy<T: Copy> {
337337
_field: crate::marker::PhantomData<T>,
338338
}
339339

0 commit comments

Comments
 (0)