Skip to content

Commit e4dd99f

Browse files
authored
Rollup merge of rust-lang#106360 - estebank:remove-borrow-suggestion, r=compiler-errors
Tweak E0277 `&`-removal suggestions Fix rust-lang#64068, fix rust-lang#84837.
2 parents 744ec5c + 8b8cce1 commit e4dd99f

17 files changed

+249
-72
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+94-34
Original file line numberDiff line numberDiff line change
@@ -1416,57 +1416,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
14161416
err: &mut Diagnostic,
14171417
trait_pred: ty::PolyTraitPredicate<'tcx>,
14181418
) -> bool {
1419-
let span = obligation.cause.span;
1419+
let mut span = obligation.cause.span;
1420+
let mut trait_pred = trait_pred;
1421+
let mut code = obligation.cause.code();
1422+
while let Some((c, Some(parent_trait_pred))) = code.parent() {
1423+
// We want the root obligation, in order to detect properly handle
1424+
// `for _ in &mut &mut vec![] {}`.
1425+
code = c;
1426+
trait_pred = parent_trait_pred;
1427+
}
1428+
while span.desugaring_kind().is_some() {
1429+
// Remove all the hir desugaring contexts while maintaining the macro contexts.
1430+
span.remove_mark();
1431+
}
1432+
let mut expr_finder = super::FindExprBySpan::new(span);
1433+
let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
1434+
return false;
1435+
};
1436+
expr_finder.visit_expr(&body);
1437+
let mut maybe_suggest = |suggested_ty, count, suggestions| {
1438+
// Remapping bound vars here
1439+
let trait_pred_and_suggested_ty =
1440+
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1441+
1442+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
1443+
obligation.param_env,
1444+
trait_pred_and_suggested_ty,
1445+
);
14201446

1421-
let mut suggested = false;
1422-
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
1423-
let refs_number =
1424-
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
1425-
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
1426-
// Do not suggest removal of borrow from type arguments.
1427-
return false;
1447+
if self.predicate_may_hold(&new_obligation) {
1448+
let msg = if count == 1 {
1449+
"consider removing the leading `&`-reference".to_string()
1450+
} else {
1451+
format!("consider removing {count} leading `&`-references")
1452+
};
1453+
1454+
err.multipart_suggestion_verbose(
1455+
&msg,
1456+
suggestions,
1457+
Applicability::MachineApplicable,
1458+
);
1459+
true
1460+
} else {
1461+
false
14281462
}
1463+
};
14291464

1430-
// Skipping binder here, remapping below
1431-
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1465+
// Maybe suggest removal of borrows from types in type parameters, like in
1466+
// `src/test/ui/not-panic/not-panic-safe.rs`.
1467+
let mut count = 0;
1468+
let mut suggestions = vec![];
1469+
// Skipping binder here, remapping below
1470+
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1471+
if let Some(mut hir_ty) = expr_finder.ty_result {
1472+
while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind {
1473+
count += 1;
1474+
let span = hir_ty.span.until(mut_ty.ty.span);
1475+
suggestions.push((span, String::new()));
14321476

1433-
for refs_remaining in 0..refs_number {
14341477
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
14351478
break;
14361479
};
14371480
suggested_ty = *inner_ty;
14381481

1439-
// Remapping bound vars here
1440-
let trait_pred_and_suggested_ty =
1441-
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1482+
hir_ty = mut_ty.ty;
14421483

1443-
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
1444-
obligation.param_env,
1445-
trait_pred_and_suggested_ty,
1446-
);
1484+
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
1485+
return true;
1486+
}
1487+
}
1488+
}
14471489

1448-
if self.predicate_may_hold(&new_obligation) {
1449-
let sp = self
1450-
.tcx
1451-
.sess
1452-
.source_map()
1453-
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
1490+
// Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1491+
let Some(mut expr) = expr_finder.result else { return false; };
1492+
let mut count = 0;
1493+
let mut suggestions = vec![];
1494+
// Skipping binder here, remapping below
1495+
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1496+
'outer: loop {
1497+
while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {
1498+
count += 1;
1499+
let span = if expr.span.eq_ctxt(borrowed.span) {
1500+
expr.span.until(borrowed.span)
1501+
} else {
1502+
expr.span.with_hi(expr.span.lo() + BytePos(1))
1503+
};
1504+
suggestions.push((span, String::new()));
14541505

1455-
let remove_refs = refs_remaining + 1;
1506+
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
1507+
break 'outer;
1508+
};
1509+
suggested_ty = *inner_ty;
14561510

1457-
let msg = if remove_refs == 1 {
1458-
"consider removing the leading `&`-reference".to_string()
1459-
} else {
1460-
format!("consider removing {} leading `&`-references", remove_refs)
1461-
};
1511+
expr = borrowed;
14621512

1463-
err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable);
1464-
suggested = true;
1465-
break;
1513+
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
1514+
return true;
14661515
}
14671516
}
1517+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
1518+
&& let hir::def::Res::Local(hir_id) = path.res
1519+
&& let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id)
1520+
&& let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
1521+
&& let None = local.ty
1522+
&& let Some(binding_expr) = local.init
1523+
{
1524+
expr = binding_expr;
1525+
} else {
1526+
break 'outer;
1527+
}
14681528
}
1469-
suggested
1529+
false
14701530
}
14711531

14721532
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {

tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ error[E0277]: the trait bound `u32: Signed` is not satisfied
44
LL | is_defaulted::<&'static u32>();
55
| ^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`
66
|
7-
= help: the trait `Signed` is implemented for `i32`
87
note: required for `&'static u32` to implement `Defaulted`
98
--> $DIR/typeck-default-trait-impl-precedence.rs:10:19
109
|
@@ -17,6 +16,11 @@ note: required by a bound in `is_defaulted`
1716
|
1817
LL | fn is_defaulted<T:Defaulted>() { }
1918
| ^^^^^^^^^ required by this bound in `is_defaulted`
19+
help: consider removing the leading `&`-reference
20+
|
21+
LL - is_defaulted::<&'static u32>();
22+
LL + is_defaulted::<u32>();
23+
|
2024

2125
error: aborting due to previous error
2226

tests/ui/impl-trait/in-trait/issue-102140.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
22
--> $DIR/issue-102140.rs:23:22
33
|
44
LL | MyTrait::foo(&self)
5-
| ------------ -^^^^
6-
| | |
7-
| | the trait `MyTrait` is not implemented for `&dyn MyTrait`
8-
| | help: consider removing the leading `&`-reference
5+
| ------------ ^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait`
6+
| |
97
| required by a bound introduced by this call
8+
|
9+
help: consider removing the leading `&`-reference
10+
|
11+
LL - MyTrait::foo(&self)
12+
LL + MyTrait::foo(self)
13+
|
1014

1115
error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
1216
--> $DIR/issue-102140.rs:23:9

tests/ui/kindck/kindck-copy.stderr

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,33 @@ error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied
44
LL | assert_copy::<&'static mut isize>();
55
| ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize`
66
|
7-
= help: the trait `Copy` is implemented for `isize`
87
note: required by a bound in `assert_copy`
98
--> $DIR/kindck-copy.rs:5:18
109
|
1110
LL | fn assert_copy<T:Copy>() { }
1211
| ^^^^ required by this bound in `assert_copy`
12+
help: consider removing the leading `&`-reference
13+
|
14+
LL - assert_copy::<&'static mut isize>();
15+
LL + assert_copy::<isize>();
16+
|
1317

1418
error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied
1519
--> $DIR/kindck-copy.rs:28:19
1620
|
1721
LL | assert_copy::<&'a mut isize>();
1822
| ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize`
1923
|
20-
= help: the trait `Copy` is implemented for `isize`
2124
note: required by a bound in `assert_copy`
2225
--> $DIR/kindck-copy.rs:5:18
2326
|
2427
LL | fn assert_copy<T:Copy>() { }
2528
| ^^^^ required by this bound in `assert_copy`
29+
help: consider removing the leading `&`-reference
30+
|
31+
LL - assert_copy::<&'a mut isize>();
32+
LL + assert_copy::<isize>();
33+
|
2634

2735
error[E0277]: the trait bound `Box<isize>: Copy` is not satisfied
2836
--> $DIR/kindck-copy.rs:31:19

tests/ui/not-panic/not-panic-safe-4.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ note: required by a bound in `assert`
1212
|
1313
LL | fn assert<T: UnwindSafe + ?Sized>() {}
1414
| ^^^^^^^^^^ required by this bound in `assert`
15+
help: consider removing the leading `&`-reference
16+
|
17+
LL - assert::<&RefCell<i32>>();
18+
LL + assert::<RefCell<i32>>();
19+
|
1520

1621
error[E0277]: the type `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
1722
--> $DIR/not-panic-safe-4.rs:9:14
@@ -28,6 +33,11 @@ note: required by a bound in `assert`
2833
|
2934
LL | fn assert<T: UnwindSafe + ?Sized>() {}
3035
| ^^^^^^^^^^ required by this bound in `assert`
36+
help: consider removing the leading `&`-reference
37+
|
38+
LL - assert::<&RefCell<i32>>();
39+
LL + assert::<RefCell<i32>>();
40+
|
3141

3242
error: aborting due to 2 previous errors
3343

tests/ui/not-panic/not-panic-safe.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ use std::panic::UnwindSafe;
55
fn assert<T: UnwindSafe + ?Sized>() {}
66

77
fn main() {
8-
assert::<&mut i32>();
9-
//~^ ERROR the type `&mut i32` may not be safely transferred across an unwind boundary
8+
assert::<&mut &mut &i32>();
9+
//~^ ERROR the type `&mut &mut &i32` may not be safely transferred across an unwind boundary
1010
}

tests/ui/not-panic/not-panic-safe.stderr

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
error[E0277]: the type `&mut i32` may not be safely transferred across an unwind boundary
1+
error[E0277]: the type `&mut &mut &i32` may not be safely transferred across an unwind boundary
22
--> $DIR/not-panic-safe.rs:8:14
33
|
4-
LL | assert::<&mut i32>();
5-
| -^^^^^^^
6-
| |
7-
| `&mut i32` may not be safely transferred across an unwind boundary
8-
| help: consider removing the leading `&`-reference
4+
LL | assert::<&mut &mut &i32>();
5+
| ^^^^^^^^^^^^^^ `&mut &mut &i32` may not be safely transferred across an unwind boundary
96
|
10-
= help: the trait `UnwindSafe` is not implemented for `&mut i32`
11-
= note: `UnwindSafe` is implemented for `&i32`, but not for `&mut i32`
7+
= help: the trait `UnwindSafe` is not implemented for `&mut &mut &i32`
8+
= note: `UnwindSafe` is implemented for `&&mut &i32`, but not for `&mut &mut &i32`
129
note: required by a bound in `assert`
1310
--> $DIR/not-panic-safe.rs:5:14
1411
|
1512
LL | fn assert<T: UnwindSafe + ?Sized>() {}
1613
| ^^^^^^^^^^ required by this bound in `assert`
14+
help: consider removing 2 leading `&`-references
15+
|
16+
LL - assert::<&mut &mut &i32>();
17+
LL + assert::<&i32>();
18+
|
1719

1820
error: aborting due to previous error
1921

tests/ui/suggestions/suggest-remove-refs-1.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ error[E0277]: `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
22
--> $DIR/suggest-remove-refs-1.rs:6:19
33
|
44
LL | for (i, _) in &v.iter().enumerate() {
5-
| -^^^^^^^^^^^^^^^^^^^^
6-
| |
7-
| `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
8-
| help: consider removing the leading `&`-reference
5+
| ^^^^^^^^^^^^^^^^^^^^^ `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
96
|
107
= help: the trait `Iterator` is not implemented for `&Enumerate<std::slice::Iter<'_, {integer}>>`
118
= note: required for `&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
9+
help: consider removing the leading `&`-reference
10+
|
11+
LL - for (i, _) in &v.iter().enumerate() {
12+
LL + for (i, _) in v.iter().enumerate() {
13+
|
1214

1315
error: aborting due to previous error
1416

tests/ui/suggestions/suggest-remove-refs-2.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ error[E0277]: `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterat
22
--> $DIR/suggest-remove-refs-2.rs:6:19
33
|
44
LL | for (i, _) in & & & & &v.iter().enumerate() {
5-
| ---------^^^^^^^^^^^^^^^^^^^^
6-
| |
7-
| `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
8-
| help: consider removing 5 leading `&`-references
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
96
|
107
= help: the trait `Iterator` is not implemented for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>`
118
= note: required for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
9+
help: consider removing 5 leading `&`-references
10+
|
11+
LL - for (i, _) in & & & & &v.iter().enumerate() {
12+
LL + for (i, _) in v.iter().enumerate() {
13+
|
1214

1315
error: aborting due to previous error
1416

tests/ui/suggestions/suggest-remove-refs-3.stderr

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
error[E0277]: `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
22
--> $DIR/suggest-remove-refs-3.rs:6:19
33
|
4-
LL | for (i, _) in & & &
5-
| ____________________^
6-
| | ___________________|
7-
| ||
8-
LL | || & &v
9-
| ||___________- help: consider removing 5 leading `&`-references
10-
LL | | .iter()
11-
LL | | .enumerate() {
12-
| |_____________________^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
4+
LL | for (i, _) in & & &
5+
| ___________________^
6+
LL | | & &v
7+
LL | | .iter()
8+
LL | | .enumerate() {
9+
| |____________________^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
1310
|
1411
= help: the trait `Iterator` is not implemented for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>`
1512
= note: required for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
13+
help: consider removing 5 leading `&`-references
14+
|
15+
LL - for (i, _) in & & &
16+
LL + for (i, _) in v
17+
|
1618

1719
error: aborting due to previous error
1820

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// run-rustfix
2+
fn main() {
3+
let foo = [1,2,3].iter();
4+
for _i in foo {} //~ ERROR E0277
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// run-rustfix
2+
fn main() {
3+
let foo = &[1,2,3].iter();
4+
for _i in &foo {} //~ ERROR E0277
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0277]: `&&std::slice::Iter<'_, {integer}>` is not an iterator
2+
--> $DIR/suggest-remove-refs-4.rs:4:15
3+
|
4+
LL | for _i in &foo {}
5+
| ^^^^ `&&std::slice::Iter<'_, {integer}>` is not an iterator
6+
|
7+
= help: the trait `Iterator` is not implemented for `&&std::slice::Iter<'_, {integer}>`
8+
= note: required for `&&std::slice::Iter<'_, {integer}>` to implement `IntoIterator`
9+
help: consider removing 2 leading `&`-references
10+
|
11+
LL ~ let foo = [1,2,3].iter();
12+
LL ~ for _i in foo {}
13+
|
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
fn main() {
3+
let v = &mut Vec::<i32>::new();
4+
for _ in v {} //~ ERROR E0277
5+
6+
let v = &mut [1u8];
7+
for _ in v {} //~ ERROR E0277
8+
}

0 commit comments

Comments
 (0)