Skip to content

Commit 6d008b2

Browse files
committed
Make refining_impl_trait suggestion wrap referenced impl-trait in parens
1 parent 3bf5c6d commit 6d008b2

4 files changed

Lines changed: 155 additions & 2 deletions

File tree

compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,31 @@ fn report_mismatched_rpitit_signature<'tcx>(
359359
// the type before creating the diagnostic, we need to avoid this query. This is the
360360
// standard approach used elsewhere in the compiler for formatting types in suggestions
361361
// (e.g., see `rustc_hir_typeck/src/demand.rs`).
362-
let return_ty_suggestion =
363-
with_no_trimmed_paths!(with_types_for_signature!(format!("{return_ty}")));
362+
//
363+
// Reference-to-opaque types need special handling: when the trait return type is
364+
// e.g. `&(impl A + B)`, the default printer emits `&impl A + B`, which parses as the
365+
// ambiguous `(&impl A) + B` and triggers "ambiguous `+` in a type" once the
366+
// suggestion is applied (cf. https://github.com/rust-lang/rust/issues/144401). Wrap
367+
// the inner opaque in parens when its printed form contains `+`-joined bounds.
368+
// Bare `impl A + B` at the top level is already syntactically valid and is left
369+
// alone; this branch only triggers for references.
370+
let return_ty_suggestion = with_no_trimmed_paths!(with_types_for_signature!({
371+
let printed = format!("{return_ty}");
372+
if let ty::Ref(_, inner, _) = return_ty.kind() {
373+
let inner_printed = format!("{inner}");
374+
if inner_printed.contains(" + ") && printed.ends_with(&inner_printed) {
375+
// Splice parens around the opaque part, keeping the original
376+
// `&`/lifetime/mutability prefix verbatim from the existing printer.
377+
let prefix_len = printed.len() - inner_printed.len();
378+
let prefix = &printed[..prefix_len];
379+
format!("{prefix}({inner_printed})")
380+
} else {
381+
printed
382+
}
383+
} else {
384+
printed
385+
}
386+
}));
364387

365388
let span = unmatched_bound.unwrap_or(span);
366389
tcx.emit_node_span_lint(
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@ run-rustfix
2+
3+
// Verify that the `refining_impl_trait_reachable` lint's suggestion wraps an
4+
// `impl A + B` in parens when it appears behind a reference, so that applying
5+
// the suggestion produces syntactically valid code rather than the ambiguous
6+
// `&impl A + B`. The bare position (`impl A + B` directly) must keep emitting
7+
// the suggestion without parens.
8+
//
9+
// https://github.com/rust-lang/rust/issues/144401
10+
11+
#![allow(unused)]
12+
#![deny(refining_impl_trait)]
13+
14+
pub trait BarA {}
15+
impl<T> BarA for T {}
16+
17+
pub trait BarB {}
18+
impl<T> BarB for T {}
19+
20+
pub struct Fool;
21+
22+
pub trait BehindRef {
23+
fn bar(&self) -> &(impl BarA + BarB);
24+
}
25+
26+
impl BehindRef for Fool {
27+
fn bar(&self) -> &(impl BarA + BarB) {
28+
//~^ ERROR impl trait in impl method signature does not match trait method signature
29+
&Fool
30+
}
31+
}
32+
33+
pub trait BarePosition {
34+
fn baz() -> impl BarA + BarB;
35+
}
36+
37+
impl BarePosition for Fool {
38+
fn baz() -> impl BarA + BarB {
39+
//~^ ERROR impl trait in impl method signature does not match trait method signature
40+
Fool
41+
}
42+
}
43+
44+
fn main() {}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@ run-rustfix
2+
3+
// Verify that the `refining_impl_trait_reachable` lint's suggestion wraps an
4+
// `impl A + B` in parens when it appears behind a reference, so that applying
5+
// the suggestion produces syntactically valid code rather than the ambiguous
6+
// `&impl A + B`. The bare position (`impl A + B` directly) must keep emitting
7+
// the suggestion without parens.
8+
//
9+
// https://github.com/rust-lang/rust/issues/144401
10+
11+
#![allow(unused)]
12+
#![deny(refining_impl_trait)]
13+
14+
pub trait BarA {}
15+
impl<T> BarA for T {}
16+
17+
pub trait BarB {}
18+
impl<T> BarB for T {}
19+
20+
pub struct Fool;
21+
22+
pub trait BehindRef {
23+
fn bar(&self) -> &(impl BarA + BarB);
24+
}
25+
26+
impl BehindRef for Fool {
27+
fn bar(&self) -> &Fool {
28+
//~^ ERROR impl trait in impl method signature does not match trait method signature
29+
&Fool
30+
}
31+
}
32+
33+
pub trait BarePosition {
34+
fn baz() -> impl BarA + BarB;
35+
}
36+
37+
impl BarePosition for Fool {
38+
fn baz() -> Fool {
39+
//~^ ERROR impl trait in impl method signature does not match trait method signature
40+
Fool
41+
}
42+
}
43+
44+
fn main() {}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: impl trait in impl method signature does not match trait method signature
2+
--> $DIR/refine-rustfix-parens.rs:27:22
3+
|
4+
LL | fn bar(&self) -> &(impl BarA + BarB);
5+
| ------------------- return type from trait method defined here
6+
...
7+
LL | fn bar(&self) -> &Fool {
8+
| ^^^^^
9+
|
10+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
11+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
12+
note: the lint level is defined here
13+
--> $DIR/refine-rustfix-parens.rs:12:9
14+
|
15+
LL | #![deny(refining_impl_trait)]
16+
| ^^^^^^^^^^^^^^^^^^^
17+
= note: `#[deny(refining_impl_trait_reachable)]` implied by `#[deny(refining_impl_trait)]`
18+
help: replace the return type so that it matches the trait
19+
|
20+
LL - fn bar(&self) -> &Fool {
21+
LL + fn bar(&self) -> &(impl BarA + BarB) {
22+
|
23+
24+
error: impl trait in impl method signature does not match trait method signature
25+
--> $DIR/refine-rustfix-parens.rs:38:17
26+
|
27+
LL | fn baz() -> impl BarA + BarB;
28+
| ---------------- return type from trait method defined here
29+
...
30+
LL | fn baz() -> Fool {
31+
| ^^^^
32+
|
33+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
34+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
35+
help: replace the return type so that it matches the trait
36+
|
37+
LL - fn baz() -> Fool {
38+
LL + fn baz() -> impl BarA + BarB {
39+
|
40+
41+
error: aborting due to 2 previous errors
42+

0 commit comments

Comments
 (0)