Skip to content

Commit fdfd2bc

Browse files
committed
Uplifts needless-maybe-sized lint from clippy
- Copies the lint and test (+ stderr) from clippy into rustc_lint/src and tests/ui/lint respectively. - Makes necessary changes to uplifted lint to work correctly in rustc. - Removes needless `?Sized` bounds from 3 files as detected and suggested by the lint.
1 parent 4f808ba commit fdfd2bc

File tree

7 files changed

+652
-4
lines changed

7 files changed

+652
-4
lines changed

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ mod lints;
6060
mod macro_expr_fragment_specifier_2024_migration;
6161
mod map_unit_fn;
6262
mod multiple_supertrait_upcastable;
63+
mod needless_maybe_sized;
6364
mod non_ascii_idents;
6465
mod non_fmt_panic;
6566
mod non_local_def;
@@ -101,6 +102,7 @@ use lifetime_syntax::*;
101102
use macro_expr_fragment_specifier_2024_migration::*;
102103
use map_unit_fn::*;
103104
use multiple_supertrait_upcastable::*;
105+
use needless_maybe_sized::NeedlessMaybeSized;
104106
use non_ascii_idents::*;
105107
use non_fmt_panic::NonPanicFmt;
106108
use non_local_def::*;
@@ -247,6 +249,7 @@ late_lint_methods!(
247249
UnqualifiedLocalImports: UnqualifiedLocalImports,
248250
CheckTransmutes: CheckTransmutes,
249251
LifetimeSyntax: LifetimeSyntax,
252+
NeedlessMaybeSized: NeedlessMaybeSized,
250253
]
251254
]
252255
);
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use rustc_errors::Applicability;
2+
use rustc_hir::def_id::{DefId, DefIdMap};
3+
use rustc_hir::{
4+
BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind,
5+
};
6+
use rustc_middle::ty::{ClauseKind, PredicatePolarity};
7+
use rustc_session::{declare_lint, declare_lint_pass};
8+
use rustc_span::symbol::Ident;
9+
10+
use crate::{LateContext, LateLintPass, LintContext};
11+
12+
declare_lint! {
13+
/// The `needless_maybe_sized` lint detects `?Sized` bounds applied to type parameters that cannot be unsized.
14+
///
15+
/// ### Example
16+
///
17+
/// ```rust
18+
/// // `T` cannot be unsized because `Clone` requires it to be `Sized`
19+
/// fn f<T: Clone + ?Sized>(t: &T) {}
20+
/// ```
21+
///
22+
/// {{produces}}
23+
///
24+
/// ### Explanation
25+
///
26+
/// The `?Sized` bound is misleading because it cannot be satisfied by an
27+
/// unsized type. This lint notifies the user of said redundant bound.
28+
pub NEEDLESS_MAYBE_SIZED,
29+
Warn,
30+
"a `?Sized` bound that is unusable due to a `Sized` requirement"
31+
}
32+
declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]);
33+
34+
struct Bound<'tcx> {
35+
/// The [`DefId`] of the type parameter the bound refers to
36+
param: DefId,
37+
ident: Ident,
38+
39+
trait_bound: &'tcx PolyTraitRef<'tcx>,
40+
41+
predicate_pos: usize,
42+
bound_pos: usize,
43+
}
44+
45+
/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion
46+
fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item = Bound<'tcx>> {
47+
generics
48+
.predicates
49+
.iter()
50+
.enumerate()
51+
.filter_map(|(predicate_pos, predicate)| {
52+
let WherePredicateKind::BoundPredicate(bound_predicate) = &predicate.kind else {
53+
return None;
54+
};
55+
56+
let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?;
57+
58+
Some(
59+
bound_predicate
60+
.bounds
61+
.iter()
62+
.enumerate()
63+
.filter_map(move |(bound_pos, bound)| match bound {
64+
GenericBound::Trait(trait_bound) => {
65+
Some(Bound { param, ident, trait_bound, predicate_pos, bound_pos })
66+
}
67+
GenericBound::Outlives(_) | GenericBound::Use(..) => None,
68+
})
69+
.filter(|bound| !bound.trait_bound.span.from_expansion()),
70+
)
71+
})
72+
.flatten()
73+
}
74+
75+
/// 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 {
79+
let trait_def_id = *path.last().unwrap();
80+
81+
if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() {
82+
return true;
83+
}
84+
85+
for (predicate, _) in
86+
cx.tcx.explicit_super_predicates_of(trait_def_id).iter_identity_copied()
87+
{
88+
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
89+
&& trait_predicate.polarity == PredicatePolarity::Positive
90+
&& !path.contains(&trait_predicate.def_id())
91+
{
92+
path.push(trait_predicate.def_id());
93+
if search(cx, path) {
94+
return true;
95+
}
96+
path.pop();
97+
}
98+
}
99+
100+
false
101+
}
102+
103+
let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
104+
search(cx, &mut path).then_some(path)
105+
}
106+
107+
impl LateLintPass<'_> for NeedlessMaybeSized {
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+
};
112+
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(NEEDLESS_MAYBE_SIZED, 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;
131+
diag.span_note(
132+
bound.trait_bound.span,
133+
format!("`{ty_param}` cannot be unsized because of the bound"),
134+
);
135+
136+
for &[current_id, next_id] in path.array_windows() {
137+
let current = cx.tcx.item_name(current_id);
138+
let next = cx.tcx.item_name(next_id);
139+
diag.note(format!("...because `{current}` has the bound `{next}`"));
140+
}
141+
142+
diag.span_suggestion_verbose(
143+
generics.span_for_bound_removal(
144+
sized_bound.predicate_pos,
145+
sized_bound.bound_pos,
146+
),
147+
"change the bounds that require `Sized`, or remove the `?Sized` bound",
148+
"",
149+
Applicability::MaybeIncorrect,
150+
);
151+
});
152+
153+
return;
154+
}
155+
}
156+
}
157+
}

library/std/src/sync/nonpoison/mutex.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ impl<T> From<T> for Mutex<T> {
389389
}
390390

391391
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
392-
impl<T: ?Sized + Default> Default for Mutex<T> {
392+
impl<T: Default> Default for Mutex<T> {
393393
/// Creates a `Mutex<T>`, with the `Default` value for T.
394394
fn default() -> Mutex<T> {
395395
Mutex::new(Default::default())

library/std/src/sync/poison/mutex.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ impl<T> From<T> for Mutex<T> {
683683
}
684684

685685
#[stable(feature = "mutex_default", since = "1.10.0")]
686-
impl<T: ?Sized + Default> Default for Mutex<T> {
686+
impl<T: Default> Default for Mutex<T> {
687687
/// Creates a `Mutex<T>`, with the `Default` value for T.
688688
fn default() -> Mutex<T> {
689689
Mutex::new(Default::default())

tests/ui/issues/issue-48728.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
//@ check-pass
55
#[derive(Clone)]
66
struct Node<T: ?Sized>(Box<T>);
7-
8-
impl<T: Clone + ?Sized> Clone for Node<[T]> {
7+
impl<T: Clone> Clone for Node<[T]> {
98
fn clone(&self) -> Self {
109
Node(Box::clone(&self.0))
1110
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#![deny(needless_maybe_sized)]
2+
3+
fn directly<T: Sized + ?Sized>(t: &T) {}
4+
//~^ ERROR needless_maybe_sized
5+
6+
trait A: Sized {}
7+
trait B: A {}
8+
9+
fn depth_1<T: A + ?Sized>(t: &T) {}
10+
//~^ ERROR needless_maybe_sized
11+
fn depth_2<T: B + ?Sized>(t: &T) {}
12+
//~^ ERROR needless_maybe_sized
13+
14+
// We only need to show one
15+
fn multiple_paths<T: A + B + ?Sized>(t: &T) {}
16+
//~^ ERROR needless_maybe_sized
17+
18+
fn in_where<T>(t: &T)
19+
where
20+
T: Sized + ?Sized,
21+
//~^ ERROR needless_maybe_sized
22+
{
23+
}
24+
25+
fn mixed_1<T: Sized>(t: &T)
26+
where
27+
T: ?Sized,
28+
//~^ ERROR needless_maybe_sized
29+
{
30+
}
31+
32+
fn mixed_2<T: ?Sized>(t: &T)
33+
//~^ ERROR needless_maybe_sized
34+
where
35+
T: Sized,
36+
{
37+
}
38+
39+
fn mixed_3<T>(t: &T)
40+
where
41+
T: Sized,
42+
T: ?Sized,
43+
//~^ ERROR needless_maybe_sized
44+
{
45+
}
46+
47+
struct Struct<T: Sized + ?Sized>(T);
48+
//~^ ERROR needless_maybe_sized
49+
50+
impl<T: Sized + ?Sized> Struct<T> {
51+
//~^ ERROR needless_maybe_sized
52+
fn method<U: Sized + ?Sized>(&self) {}
53+
//~^ ERROR needless_maybe_sized
54+
}
55+
56+
enum Enum<T: Sized + ?Sized + 'static> {
57+
//~^ ERROR needless_maybe_sized
58+
Variant(&'static T),
59+
}
60+
61+
union Union<'a, T: Sized + ?Sized> {
62+
//~^ ERROR needless_maybe_sized
63+
a: &'a T,
64+
}
65+
66+
trait Trait<T: Sized + ?Sized> {
67+
//~^ ERROR needless_maybe_sized
68+
fn trait_method<U: Sized + ?Sized>() {}
69+
//~^ ERROR needless_maybe_sized
70+
71+
type GAT<U: Sized + ?Sized>;
72+
//~^ ERROR needless_maybe_sized
73+
74+
type Assoc: Sized + ?Sized; // False negative
75+
}
76+
77+
trait SecondInTrait: Send + Sized {}
78+
fn second_in_trait<T: ?Sized + SecondInTrait>() {}
79+
//~^ ERROR needless_maybe_sized
80+
81+
fn impl_trait(_: &(impl Sized + ?Sized)) {}
82+
//~^ ERROR needless_maybe_sized
83+
84+
trait GenericTrait<T>: Sized {}
85+
fn in_generic_trait<T: GenericTrait<U> + ?Sized, U>() {}
86+
//~^ ERROR needless_maybe_sized
87+
88+
mod larger_graph {
89+
// C1 C2 Sized
90+
// \ /\ /
91+
// B1 B2
92+
// \ /
93+
// A1
94+
95+
trait C1 {}
96+
trait C2 {}
97+
trait B1: C1 + C2 {}
98+
trait B2: C2 + Sized {}
99+
trait A1: B1 + B2 {}
100+
101+
fn larger_graph<T: A1 + ?Sized>() {}
102+
//~^ ERROR needless_maybe_sized
103+
}
104+
105+
// Should not lint
106+
107+
fn sized<T: Sized>() {}
108+
fn maybe_sized<T: ?Sized>() {}
109+
110+
struct SeparateBounds<T: ?Sized>(T);
111+
impl<T: Sized> SeparateBounds<T> {}
112+
113+
trait P {}
114+
trait Q: P {}
115+
116+
fn ok_depth_1<T: P + ?Sized>() {}
117+
fn ok_depth_2<T: Q + ?Sized>() {}
118+
119+
//external! {
120+
// fn in_macro<T: Clone + ?Sized>(t: &T) {}
121+
122+
// fn with_local_clone<T: $Clone + ?Sized>(t: &T) {}
123+
//}
124+
125+
#[derive(Clone)]
126+
struct InDerive<T: ?Sized> {
127+
t: T,
128+
}
129+
130+
struct Refined<T: ?Sized>(T);
131+
impl<T: Sized> Refined<T> {}
132+
133+
fn main() {}

0 commit comments

Comments
 (0)