Skip to content

Commit 54f6fea

Browse files
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 244b90e + 8b8cce1 commit 54f6fea

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
@@ -1361,57 +1361,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
13611361
err: &mut Diagnostic,
13621362
trait_pred: ty::PolyTraitPredicate<'tcx>,
13631363
) -> bool {
1364-
let span = obligation.cause.span;
1364+
let mut span = obligation.cause.span;
1365+
let mut trait_pred = trait_pred;
1366+
let mut code = obligation.cause.code();
1367+
while let Some((c, Some(parent_trait_pred))) = code.parent() {
1368+
// We want the root obligation, in order to detect properly handle
1369+
// `for _ in &mut &mut vec![] {}`.
1370+
code = c;
1371+
trait_pred = parent_trait_pred;
1372+
}
1373+
while span.desugaring_kind().is_some() {
1374+
// Remove all the hir desugaring contexts while maintaining the macro contexts.
1375+
span.remove_mark();
1376+
}
1377+
let mut expr_finder = super::FindExprBySpan::new(span);
1378+
let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
1379+
return false;
1380+
};
1381+
expr_finder.visit_expr(&body);
1382+
let mut maybe_suggest = |suggested_ty, count, suggestions| {
1383+
// Remapping bound vars here
1384+
let trait_pred_and_suggested_ty =
1385+
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1386+
1387+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
1388+
obligation.param_env,
1389+
trait_pred_and_suggested_ty,
1390+
);
13651391

1366-
let mut suggested = false;
1367-
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
1368-
let refs_number =
1369-
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
1370-
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
1371-
// Do not suggest removal of borrow from type arguments.
1372-
return false;
1392+
if self.predicate_may_hold(&new_obligation) {
1393+
let msg = if count == 1 {
1394+
"consider removing the leading `&`-reference".to_string()
1395+
} else {
1396+
format!("consider removing {count} leading `&`-references")
1397+
};
1398+
1399+
err.multipart_suggestion_verbose(
1400+
&msg,
1401+
suggestions,
1402+
Applicability::MachineApplicable,
1403+
);
1404+
true
1405+
} else {
1406+
false
13731407
}
1408+
};
13741409

1375-
// Skipping binder here, remapping below
1376-
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1410+
// Maybe suggest removal of borrows from types in type parameters, like in
1411+
// `src/test/ui/not-panic/not-panic-safe.rs`.
1412+
let mut count = 0;
1413+
let mut suggestions = vec![];
1414+
// Skipping binder here, remapping below
1415+
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1416+
if let Some(mut hir_ty) = expr_finder.ty_result {
1417+
while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind {
1418+
count += 1;
1419+
let span = hir_ty.span.until(mut_ty.ty.span);
1420+
suggestions.push((span, String::new()));
13771421

1378-
for refs_remaining in 0..refs_number {
13791422
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
13801423
break;
13811424
};
13821425
suggested_ty = *inner_ty;
13831426

1384-
// Remapping bound vars here
1385-
let trait_pred_and_suggested_ty =
1386-
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1427+
hir_ty = mut_ty.ty;
13871428

1388-
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
1389-
obligation.param_env,
1390-
trait_pred_and_suggested_ty,
1391-
);
1429+
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
1430+
return true;
1431+
}
1432+
}
1433+
}
13921434

1393-
if self.predicate_may_hold(&new_obligation) {
1394-
let sp = self
1395-
.tcx
1396-
.sess
1397-
.source_map()
1398-
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
1435+
// Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1436+
let Some(mut expr) = expr_finder.result else { return false; };
1437+
let mut count = 0;
1438+
let mut suggestions = vec![];
1439+
// Skipping binder here, remapping below
1440+
let mut suggested_ty = trait_pred.self_ty().skip_binder();
1441+
'outer: loop {
1442+
while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {
1443+
count += 1;
1444+
let span = if expr.span.eq_ctxt(borrowed.span) {
1445+
expr.span.until(borrowed.span)
1446+
} else {
1447+
expr.span.with_hi(expr.span.lo() + BytePos(1))
1448+
};
1449+
suggestions.push((span, String::new()));
13991450

1400-
let remove_refs = refs_remaining + 1;
1451+
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
1452+
break 'outer;
1453+
};
1454+
suggested_ty = *inner_ty;
14011455

1402-
let msg = if remove_refs == 1 {
1403-
"consider removing the leading `&`-reference".to_string()
1404-
} else {
1405-
format!("consider removing {} leading `&`-references", remove_refs)
1406-
};
1456+
expr = borrowed;
14071457

1408-
err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable);
1409-
suggested = true;
1410-
break;
1458+
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
1459+
return true;
14111460
}
14121461
}
1462+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
1463+
&& let hir::def::Res::Local(hir_id) = path.res
1464+
&& let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id)
1465+
&& let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
1466+
&& let None = local.ty
1467+
&& let Some(binding_expr) = local.init
1468+
{
1469+
expr = binding_expr;
1470+
} else {
1471+
break 'outer;
1472+
}
14131473
}
1414-
suggested
1474+
false
14151475
}
14161476

14171477
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)