Skip to content

Commit 563abf9

Browse files
committedAug 30, 2023
[implied_bounds_in_impls]: move to nursery and fix ICEs
1 parent b97eaab commit 563abf9

7 files changed

+203
-41
lines changed
 

‎clippy_lints/src/implied_bounds_in_impls.rs

+77-40
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
33
use rustc_errors::{Applicability, SuggestionStyle};
4-
use rustc_hir::def_id::LocalDefId;
4+
use rustc_hir::def_id::{DefId, LocalDefId};
55
use rustc_hir::intravisit::FnKind;
66
use rustc_hir::{
77
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
88
TraitItemKind, TyKind,
99
};
1010
use rustc_hir_analysis::hir_ty_to_ty;
1111
use rustc_lint::{LateContext, LateLintPass};
12-
use rustc_middle::ty::{self, ClauseKind, TyCtxt};
12+
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
1313
use rustc_session::{declare_lint_pass, declare_tool_lint};
1414
use rustc_span::Span;
1515

@@ -45,52 +45,80 @@ declare_clippy_lint! {
4545
/// ```
4646
#[clippy::version = "1.73.0"]
4747
pub IMPLIED_BOUNDS_IN_IMPLS,
48-
complexity,
48+
nursery,
4949
"specifying bounds that are implied by other bounds in `impl Trait` type"
5050
}
5151
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
5252

53-
/// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
54-
/// check if the substituted type in the implied-by bound matches with what's subtituted in the
55-
/// implied type.
53+
/// Tries to "resolve" a type.
54+
/// The index passed to this function must start with `Self=0`, i.e. it must be a valid
55+
/// type parameter index.
56+
/// If the index is out of bounds, it means that the generic parameter has a default type.
57+
fn try_resolve_type<'tcx>(
58+
tcx: TyCtxt<'tcx>,
59+
args: &'tcx [GenericArg<'tcx>],
60+
generics: &'tcx Generics,
61+
index: usize,
62+
) -> Option<Ty<'tcx>> {
63+
match args.get(index - 1) {
64+
Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)),
65+
Some(_) => None,
66+
None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()),
67+
}
68+
}
69+
70+
/// This function tries to, for all generic type parameters in a supertrait predicate `trait ...<U>:
71+
/// GenericTrait<U>`, check if the substituted type in the implied-by bound matches with what's
72+
/// subtituted in the implied bound.
5673
///
5774
/// Consider this example.
5875
/// ```rust,ignore
5976
/// trait GenericTrait<T> {}
6077
/// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
61-
/// ^ trait_predicate_args: [Self#0, U#2]
78+
/// ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2]
79+
/// (the Self#0 is implicit: `<Self as GenericTrait<U>>`)
6280
/// impl GenericTrait<i32> for () {}
6381
/// impl GenericSubTrait<(), i32, ()> for () {}
64-
/// impl GenericSubTrait<(), [u8; 8], ()> for () {}
82+
/// impl GenericSubTrait<(), i64, ()> for () {}
6583
///
66-
/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
67-
/// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args
68-
/// (we are interested in `[u8; 8]` specifically, as that
69-
/// is what `U` in `GenericTrait<U>` is substituted with)
70-
/// ()
84+
/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), i64, ()> {
85+
/// ^^^ implied_args ^^^^^^^^^^^ implied_by_args
86+
/// (we are interested in `i64` specifically, as that
87+
/// is what `U` in `GenericTrait<U>` is substituted with)
7188
/// }
7289
/// ```
73-
/// Here i32 != [u8; 8], so this will return false.
74-
fn is_same_generics(
75-
tcx: TyCtxt<'_>,
76-
trait_predicate_args: &[ty::GenericArg<'_>],
77-
implied_by_args: &[GenericArg<'_>],
78-
implied_args: &[GenericArg<'_>],
90+
/// Here i32 != i64, so this will return false.
91+
fn is_same_generics<'tcx>(
92+
tcx: TyCtxt<'tcx>,
93+
trait_predicate_args: &'tcx [ty::GenericArg<'tcx>],
94+
implied_by_args: &'tcx [GenericArg<'tcx>],
95+
implied_args: &'tcx [GenericArg<'tcx>],
96+
implied_by_def_id: DefId,
97+
implied_def_id: DefId,
7998
) -> bool {
99+
// Get the generics of the two traits to be able to get default generic parameter.
100+
let implied_by_generics = tcx.generics_of(implied_by_def_id);
101+
let implied_generics = tcx.generics_of(implied_def_id);
102+
80103
trait_predicate_args
81104
.iter()
82105
.enumerate()
83106
.skip(1) // skip `Self` implicit arg
84107
.all(|(arg_index, arg)| {
85-
if let Some(ty) = arg.as_type()
86-
&& let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind()
87-
// Since `trait_predicate_args` and type params in traits start with `Self=0`
88-
// and generic argument lists `GenericTrait<i32>` don't have `Self`,
89-
// we need to subtract 1 from the index.
90-
&& let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1]
91-
&& let GenericArg::Type(ty_b) = implied_args[arg_index - 1]
92-
{
93-
hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b)
108+
if let Some(ty) = arg.as_type() {
109+
if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind()
110+
// `index == 0` means that it's referring to `Self`,
111+
// in which case we don't try to substitute it
112+
&& index != 0
113+
&& let Some(ty_a) = try_resolve_type(tcx, implied_by_args, implied_by_generics, index as usize)
114+
&& let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index)
115+
{
116+
ty_a == ty_b
117+
} else if let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) {
118+
ty == ty_b
119+
} else {
120+
false
121+
}
94122
} else {
95123
false
96124
}
@@ -121,7 +149,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
121149
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
122150
&& !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
123151
{
124-
Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates))
152+
Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates, trait_def_id))
125153
} else {
126154
None
127155
}
@@ -135,18 +163,27 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
135163
&& let [.., path] = poly_trait.trait_ref.path.segments
136164
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
137165
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
138-
&& let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| {
139-
preds.iter().find_map(|(clause, _)| {
140-
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
141-
&& tr.def_id() == def_id
142-
&& is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args)
143-
{
144-
Some(span)
145-
} else {
146-
None
147-
}
166+
&& let Some(implied_by_span) = implied_bounds
167+
.iter()
168+
.find_map(|&(span, implied_by_args, preds, implied_by_def_id)| {
169+
preds.iter().find_map(|(clause, _)| {
170+
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
171+
&& tr.def_id() == def_id
172+
&& is_same_generics(
173+
cx.tcx,
174+
tr.trait_ref.args,
175+
implied_by_args,
176+
implied_args,
177+
implied_by_def_id,
178+
def_id,
179+
)
180+
{
181+
Some(span)
182+
} else {
183+
None
184+
}
185+
})
148186
})
149-
})
150187
{
151188
let implied_by = snippet(cx, implied_by_span, "..");
152189
span_lint_and_then(

‎tests/ui/crashes/ice-11422.fixed

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![warn(clippy::implied_bounds_in_impls)]
2+
3+
use std::fmt::Debug;
4+
use std::ops::*;
5+
6+
fn gen() -> impl PartialOrd + Debug {}
7+
8+
struct Bar {}
9+
trait Foo<T = Self> {}
10+
trait FooNested<T = Option<Self>> {}
11+
impl Foo for Bar {}
12+
impl FooNested for Bar {}
13+
14+
fn foo() -> impl Foo + FooNested {
15+
Bar {}
16+
}
17+
18+
fn test_impl_ops() -> impl Add + Sub + Mul + Div {
19+
1
20+
}
21+
fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign {
22+
1
23+
}
24+
25+
fn main() {}

‎tests/ui/crashes/ice-11422.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![warn(clippy::implied_bounds_in_impls)]
2+
3+
use std::fmt::Debug;
4+
use std::ops::*;
5+
6+
fn gen() -> impl PartialOrd + PartialEq + Debug {}
7+
8+
struct Bar {}
9+
trait Foo<T = Self> {}
10+
trait FooNested<T = Option<Self>> {}
11+
impl Foo for Bar {}
12+
impl FooNested for Bar {}
13+
14+
fn foo() -> impl Foo + FooNested {
15+
Bar {}
16+
}
17+
18+
fn test_impl_ops() -> impl Add + Sub + Mul + Div {
19+
1
20+
}
21+
fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign {
22+
1
23+
}
24+
25+
fn main() {}

‎tests/ui/crashes/ice-11422.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: this bound is already specified as the supertrait of `PartialOrd`
2+
--> $DIR/ice-11422.rs:6:31
3+
|
4+
LL | fn gen() -> impl PartialOrd + PartialEq + Debug {}
5+
| ^^^^^^^^^
6+
|
7+
= note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings`
8+
help: try removing this bound
9+
|
10+
LL - fn gen() -> impl PartialOrd + PartialEq + Debug {}
11+
LL + fn gen() -> impl PartialOrd + Debug {}
12+
|
13+
14+
error: aborting due to previous error
15+

‎tests/ui/implied_bounds_in_impls.fixed

+18
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,22 @@ impl SomeTrait for SomeStruct {
6565
}
6666
}
6767

68+
mod issue11422 {
69+
use core::fmt::Debug;
70+
// Some additional tests that would cause ICEs:
71+
72+
// `PartialOrd` has a default generic parameter and does not need to be explicitly specified.
73+
// This needs special handling.
74+
fn default_generic_param1() -> impl PartialOrd + Debug {}
75+
fn default_generic_param2() -> impl PartialOrd + Debug {}
76+
77+
// Referring to `Self` in the supertrait clause needs special handling.
78+
trait Trait1<X: ?Sized> {}
79+
trait Trait2: Trait1<Self> {}
80+
impl Trait1<()> for () {}
81+
impl Trait2 for () {}
82+
83+
fn f() -> impl Trait1<()> + Trait2 {}
84+
}
85+
6886
fn main() {}

‎tests/ui/implied_bounds_in_impls.rs

+18
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,22 @@ impl SomeTrait for SomeStruct {
6565
}
6666
}
6767

68+
mod issue11422 {
69+
use core::fmt::Debug;
70+
// Some additional tests that would cause ICEs:
71+
72+
// `PartialOrd` has a default generic parameter and does not need to be explicitly specified.
73+
// This needs special handling.
74+
fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
75+
fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
76+
77+
// Referring to `Self` in the supertrait clause needs special handling.
78+
trait Trait1<X: ?Sized> {}
79+
trait Trait2: Trait1<Self> {}
80+
impl Trait1<()> for () {}
81+
impl Trait2 for () {}
82+
83+
fn f() -> impl Trait1<()> + Trait2 {}
84+
}
85+
6886
fn main() {}

‎tests/ui/implied_bounds_in_impls.stderr

+25-1
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,29 @@ LL - fn f() -> impl Deref + DerefMut<Target = u8> {
119119
LL + fn f() -> impl DerefMut<Target = u8> {
120120
|
121121

122-
error: aborting due to 10 previous errors
122+
error: this bound is already specified as the supertrait of `PartialOrd`
123+
--> $DIR/implied_bounds_in_impls.rs:74:41
124+
|
125+
LL | fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
126+
| ^^^^^^^^^
127+
|
128+
help: try removing this bound
129+
|
130+
LL - fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
131+
LL + fn default_generic_param1() -> impl PartialOrd + Debug {}
132+
|
133+
134+
error: this bound is already specified as the supertrait of `PartialOrd`
135+
--> $DIR/implied_bounds_in_impls.rs:75:54
136+
|
137+
LL | fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
138+
| ^^^^^^^^^
139+
|
140+
help: try removing this bound
141+
|
142+
LL - fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
143+
LL + fn default_generic_param2() -> impl PartialOrd + Debug {}
144+
|
145+
146+
error: aborting due to 12 previous errors
123147

0 commit comments

Comments
 (0)
Please sign in to comment.