Rollup merge of #156843 - onehr:fix-redundant-captures-143216, r=cjgillot

Make impl_trait_redundant_captures suggestion remove adjacent +

Closes rust-lang/rust#143216

The `impl_trait_redundant_captures` lint's machine-applicable suggestion only
spanned the `use<...>` syntax itself, leaving the adjacent `+` joiner behind.
Applying the suggestion produced uncompilable code, e.g.
`impl Sized + use<>` becoming `impl Sized + ` (stray trailing `+`).

This extends the removal span to also cover one adjacent `+`, preserving
valid syntax in the three bound-list positions covered by the regression test:

- `impl Sized + use<>` becomes `impl Sized`
- `impl use<> + Sized` becomes `impl Sized`
- `impl Sized + use<> + Send` becomes `impl Sized + Send`

A `//@ run-rustfix` UI test exercises all three positions, so the
rustfix-applied output is actually compiled — covering the gap the
existing `redundant.rs` test left (which only checks lint firing, not
suggestion correctness).

Tested:
- ./x test tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs --bless
- ./x test tidy
This commit is contained in:
Jacob Pratt
2026-05-24 16:39:43 +02:00
committed by GitHub
5 changed files with 118 additions and 16 deletions
@@ -361,9 +361,9 @@ fn visit_ty(&mut self, t: Ty<'tcx>) {
// have no uncaptured args, then we should warn to the user that
// it's redundant to capture all args explicitly.
if new_capture_rules
&& let Some((captured_args, capturing_span)) =
opaque.bounds.iter().find_map(|bound| match *bound {
hir::GenericBound::Use(a, s) => Some((a, s)),
&& let Some((use_idx, captured_args, capturing_span)) =
opaque.bounds.iter().enumerate().find_map(|(i, bound)| match *bound {
hir::GenericBound::Use(a, s) => Some((i, a, s)),
_ => None,
})
{
@@ -400,11 +400,25 @@ fn visit_ty(&mut self, t: Ty<'tcx>) {
.iter()
.all(|(def_id, _)| explicitly_captured.contains(def_id))
{
// Extend the removal span to include the `+` joiner adjacent
// to `use<...>`, so applying the suggestion does not leave
// behind a stray `+` that fails to parse.
let suggestion_span = if let Some(next) = opaque.bounds.get(use_idx + 1) {
capturing_span.with_hi(next.span().lo())
} else if let Some(prev_idx) = use_idx.checked_sub(1) {
let prev = opaque.bounds[prev_idx];
capturing_span.with_lo(prev.span().hi())
} else {
// `impl use<...>` with no other bound is not valid
// syntax, so this branch is unreachable in practice.
capturing_span
};
self.tcx.emit_node_span_lint(
IMPL_TRAIT_REDUNDANT_CAPTURES,
self.tcx.local_def_id_to_hir_id(opaque_def_id),
opaque_span,
ImplTraitRedundantCapturesLint { capturing_span },
ImplTraitRedundantCapturesLint { capturing_span: suggestion_span },
);
}
}
@@ -0,0 +1,28 @@
//@ run-rustfix
//@ rustfix-only-machine-applicable
//@ edition: 2024
// Verify that the suggestion produced by `impl_trait_redundant_captures`
// removes the adjacent `+` joiner along with `use<...>`, instead of leaving
// behind a stray `+` that fails to compile. Regression test for
// https://github.com/rust-lang/rust/issues/143216.
#![allow(unused)]
#![deny(impl_trait_redundant_captures)]
// `use<>` at the end of the bound list: the suggestion must remove the
// preceding `+`.
fn end_position() -> impl Sized {}
//~^ ERROR all possible in-scope parameters are already captured
// `use<>` at the start of the bound list: the suggestion must remove the
// following `+`.
fn start_position() -> impl Sized {}
//~^ ERROR all possible in-scope parameters are already captured
// `use<>` in the middle of the bound list: the suggestion must remove
// exactly one `+`, keeping the other to join the remaining bounds.
fn middle_position() -> impl Sized + Send {}
//~^ ERROR all possible in-scope parameters are already captured
fn main() {}
@@ -0,0 +1,28 @@
//@ run-rustfix
//@ rustfix-only-machine-applicable
//@ edition: 2024
// Verify that the suggestion produced by `impl_trait_redundant_captures`
// removes the adjacent `+` joiner along with `use<...>`, instead of leaving
// behind a stray `+` that fails to compile. Regression test for
// https://github.com/rust-lang/rust/issues/143216.
#![allow(unused)]
#![deny(impl_trait_redundant_captures)]
// `use<>` at the end of the bound list: the suggestion must remove the
// preceding `+`.
fn end_position() -> impl Sized + use<> {}
//~^ ERROR all possible in-scope parameters are already captured
// `use<>` at the start of the bound list: the suggestion must remove the
// following `+`.
fn start_position() -> impl use<> + Sized {}
//~^ ERROR all possible in-scope parameters are already captured
// `use<>` in the middle of the bound list: the suggestion must remove
// exactly one `+`, keeping the other to join the remaining bounds.
fn middle_position() -> impl Sized + use<> + Send {}
//~^ ERROR all possible in-scope parameters are already captured
fn main() {}
@@ -0,0 +1,32 @@
error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant-machine-applicable.rs:15:22
|
LL | fn end_position() -> impl Sized + use<> {}
| ^^^^^^^^^^--------
| |
| help: remove the `use<...>` syntax
|
note: the lint level is defined here
--> $DIR/redundant-machine-applicable.rs:11:9
|
LL | #![deny(impl_trait_redundant_captures)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant-machine-applicable.rs:20:24
|
LL | fn start_position() -> impl use<> + Sized {}
| ^^^^^--------^^^^^
| |
| help: remove the `use<...>` syntax
error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant-machine-applicable.rs:25:25
|
LL | fn middle_position() -> impl Sized + use<> + Send {}
| ^^^^^^^^^^^^^--------^^^^
| |
| help: remove the `use<...>` syntax
error: aborting due to 3 previous errors
@@ -2,9 +2,9 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt
--> $DIR/redundant.rs:5:19
|
LL | fn hello<'a>() -> impl Sized + use<'a> {}
| ^^^^^^^^^^^^^-------
| |
| help: remove the `use<...>` syntax
| ^^^^^^^^^^----------
| |
| help: remove the `use<...>` syntax
|
note: the lint level is defined here
--> $DIR/redundant.rs:3:9
@@ -16,25 +16,25 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt
--> $DIR/redundant.rs:10:27
|
LL | fn inherent(&self) -> impl Sized + use<'_> {}
| ^^^^^^^^^^^^^-------
| |
| help: remove the `use<...>` syntax
| ^^^^^^^^^^----------
| |
| help: remove the `use<...>` syntax
error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:15:22
|
LL | fn in_trait() -> impl Sized + use<'a, Self>;
| ^^^^^^^^^^^^^-------------
| |
| help: remove the `use<...>` syntax
| ^^^^^^^^^^----------------
| |
| help: remove the `use<...>` syntax
error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:19:22
|
LL | fn in_trait() -> impl Sized + use<'a> {}
| ^^^^^^^^^^^^^-------
| |
| help: remove the `use<...>` syntax
| ^^^^^^^^^^----------
| |
| help: remove the `use<...>` syntax
error: aborting due to 4 previous errors