Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweak E0277 &-removal suggestions #106360

Merged
merged 4 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1358,57 +1358,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
let span = obligation.cause.span;
let mut span = obligation.cause.span;
let mut trait_pred = trait_pred;
let mut code = obligation.cause.code();
while let Some((c, Some(parent_trait_pred))) = code.parent() {
// We want the root obligation, in order to detect properly handle
// `for _ in &mut &mut vec![] {}`.
code = c;
trait_pred = parent_trait_pred;
}
Comment on lines +1364 to +1369
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there some sort of peel method that does this lol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a few (blame me for the nomenclature :P) but I think not for this ^_^'

while span.desugaring_kind().is_some() {
// Remove all the hir desugaring contexts while maintaining the macro contexts.
span.remove_mark();
}
let mut expr_finder = super::FindExprBySpan::new(span);
let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
return false;
};
expr_finder.visit_expr(&body);
let mut maybe_suggest = |suggested_ty, count, suggestions| {
// Remapping bound vars here
let trait_pred_and_suggested_ty =
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));

let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred_and_suggested_ty,
);

let mut suggested = false;
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let refs_number =
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
// Do not suggest removal of borrow from type arguments.
return false;
if self.predicate_may_hold(&new_obligation) {
let msg = if count == 1 {
"consider removing the leading `&`-reference".to_string()
} else {
format!("consider removing {count} leading `&`-references")
};

err.multipart_suggestion_verbose(
&msg,
suggestions,
Applicability::MachineApplicable,
);
true
} else {
false
}
};

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

for refs_remaining in 0..refs_number {
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
break;
};
suggested_ty = *inner_ty;

// Remapping bound vars here
let trait_pred_and_suggested_ty =
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
hir_ty = mut_ty.ty;

let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred_and_suggested_ty,
);
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
return true;
}
}
}

if self.predicate_may_hold(&new_obligation) {
let sp = self
.tcx
.sess
.source_map()
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
// Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
let Some(mut expr) = expr_finder.result else { return false; };
let mut count = 0;
let mut suggestions = vec![];
// Skipping binder here, remapping below
let mut suggested_ty = trait_pred.self_ty().skip_binder();
'outer: loop {
while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {
count += 1;
let span = if expr.span.eq_ctxt(borrowed.span) {
expr.span.until(borrowed.span)
} else {
expr.span.with_hi(expr.span.lo() + BytePos(1))
};
suggestions.push((span, String::new()));

let remove_refs = refs_remaining + 1;
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
break 'outer;
};
suggested_ty = *inner_ty;

let msg = if remove_refs == 1 {
"consider removing the leading `&`-reference".to_string()
} else {
format!("consider removing {} leading `&`-references", remove_refs)
};
expr = borrowed;

err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable);
suggested = true;
break;
if maybe_suggest(suggested_ty, count, suggestions.clone()) {
return true;
}
}
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
&& let hir::def::Res::Local(hir_id) = path.res
&& let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id)
&& let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
&& let None = local.ty
&& let Some(binding_expr) = local.init
{
expr = binding_expr;
} else {
break 'outer;
}
}
suggested
false
}

fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ error[E0277]: the trait bound `u32: Signed` is not satisfied
LL | is_defaulted::<&'static u32>();
| ^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`
|
= help: the trait `Signed` is implemented for `i32`
note: required for `&'static u32` to implement `Defaulted`
--> $DIR/typeck-default-trait-impl-precedence.rs:10:19
|
Expand All @@ -15,6 +14,11 @@ note: required by a bound in `is_defaulted`
|
LL | fn is_defaulted<T:Defaulted>() { }
| ^^^^^^^^^ required by this bound in `is_defaulted`
help: consider removing the leading `&`-reference
|
LL - is_defaulted::<&'static u32>();
LL + is_defaulted::<u32>();
|

error: aborting due to previous error

Expand Down
12 changes: 8 additions & 4 deletions tests/ui/impl-trait/in-trait/issue-102140.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
--> $DIR/issue-102140.rs:23:22
|
LL | MyTrait::foo(&self)
| ------------ -^^^^
| | |
| | the trait `MyTrait` is not implemented for `&dyn MyTrait`
| | help: consider removing the leading `&`-reference
| ------------ ^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait`
| |
| required by a bound introduced by this call
|
help: consider removing the leading `&`-reference
|
LL - MyTrait::foo(&self)
LL + MyTrait::foo(self)
|

error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
--> $DIR/issue-102140.rs:23:9
Expand Down
12 changes: 10 additions & 2 deletions tests/ui/kindck/kindck-copy.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,33 @@ error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied
LL | assert_copy::<&'static mut isize>();
| ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize`
|
= help: the trait `Copy` is implemented for `isize`
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
LL | fn assert_copy<T:Copy>() { }
| ^^^^ required by this bound in `assert_copy`
help: consider removing the leading `&`-reference
|
LL - assert_copy::<&'static mut isize>();
LL + assert_copy::<isize>();
|

error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied
--> $DIR/kindck-copy.rs:28:19
|
LL | assert_copy::<&'a mut isize>();
| ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize`
|
= help: the trait `Copy` is implemented for `isize`
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
LL | fn assert_copy<T:Copy>() { }
| ^^^^ required by this bound in `assert_copy`
help: consider removing the leading `&`-reference
|
LL - assert_copy::<&'a mut isize>();
LL + assert_copy::<isize>();
|

error[E0277]: the trait bound `Box<isize>: Copy` is not satisfied
--> $DIR/kindck-copy.rs:31:19
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/not-panic/not-panic-safe-4.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ note: required by a bound in `assert`
|
LL | fn assert<T: UnwindSafe + ?Sized>() {}
| ^^^^^^^^^^ required by this bound in `assert`
help: consider removing the leading `&`-reference
|
LL - assert::<&RefCell<i32>>();
LL + assert::<RefCell<i32>>();
|

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

error: aborting due to 2 previous errors

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/not-panic/not-panic-safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ use std::panic::UnwindSafe;
fn assert<T: UnwindSafe + ?Sized>() {}

fn main() {
assert::<&mut i32>();
//~^ ERROR the type `&mut i32` may not be safely transferred across an unwind boundary
assert::<&mut &mut &i32>();
//~^ ERROR the type `&mut &mut &i32` may not be safely transferred across an unwind boundary
}
18 changes: 10 additions & 8 deletions tests/ui/not-panic/not-panic-safe.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
error[E0277]: the type `&mut i32` may not be safely transferred across an unwind boundary
error[E0277]: the type `&mut &mut &i32` may not be safely transferred across an unwind boundary
--> $DIR/not-panic-safe.rs:8:14
|
LL | assert::<&mut i32>();
| -^^^^^^^
| |
| `&mut i32` may not be safely transferred across an unwind boundary
| help: consider removing the leading `&`-reference
LL | assert::<&mut &mut &i32>();
| ^^^^^^^^^^^^^^ `&mut &mut &i32` may not be safely transferred across an unwind boundary
|
= help: the trait `UnwindSafe` is not implemented for `&mut i32`
= note: `UnwindSafe` is implemented for `&i32`, but not for `&mut i32`
= help: the trait `UnwindSafe` is not implemented for `&mut &mut &i32`
= note: `UnwindSafe` is implemented for `&&mut &i32`, but not for `&mut &mut &i32`
note: required by a bound in `assert`
--> $DIR/not-panic-safe.rs:5:14
|
LL | fn assert<T: UnwindSafe + ?Sized>() {}
| ^^^^^^^^^^ required by this bound in `assert`
help: consider removing 2 leading `&`-references
|
LL - assert::<&mut &mut &i32>();
LL + assert::<&i32>();
|

error: aborting due to previous error

Expand Down
10 changes: 6 additions & 4 deletions tests/ui/suggestions/suggest-remove-refs-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ error[E0277]: `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
--> $DIR/suggest-remove-refs-1.rs:6:19
|
LL | for (i, _) in &v.iter().enumerate() {
| -^^^^^^^^^^^^^^^^^^^^
| |
| `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
| help: consider removing the leading `&`-reference
| ^^^^^^^^^^^^^^^^^^^^^ `&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&Enumerate<std::slice::Iter<'_, {integer}>>`
= note: required for `&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
help: consider removing the leading `&`-reference
|
LL - for (i, _) in &v.iter().enumerate() {
LL + for (i, _) in v.iter().enumerate() {
|

error: aborting due to previous error

Expand Down
10 changes: 6 additions & 4 deletions tests/ui/suggestions/suggest-remove-refs-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ error[E0277]: `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterat
--> $DIR/suggest-remove-refs-2.rs:6:19
|
LL | for (i, _) in & & & & &v.iter().enumerate() {
| ---------^^^^^^^^^^^^^^^^^^^^
| |
| `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
| help: consider removing 5 leading `&`-references
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>`
= note: required for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
help: consider removing 5 leading `&`-references
|
LL - for (i, _) in & & & & &v.iter().enumerate() {
LL + for (i, _) in v.iter().enumerate() {
|

error: aborting due to previous error

Expand Down
20 changes: 11 additions & 9 deletions tests/ui/suggestions/suggest-remove-refs-3.stderr
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
error[E0277]: `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
--> $DIR/suggest-remove-refs-3.rs:6:19
|
LL | for (i, _) in & & &
| ____________________^
| | ___________________|
| ||
LL | || & &v
| ||___________- help: consider removing 5 leading `&`-references
LL | | .iter()
LL | | .enumerate() {
| |_____________________^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
LL | for (i, _) in & & &
| ___________________^
LL | | & &v
LL | | .iter()
LL | | .enumerate() {
| |____________________^ `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>`
= note: required for `&&&&&Enumerate<std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
help: consider removing 5 leading `&`-references
|
LL - for (i, _) in & & &
LL + for (i, _) in v
|

error: aborting due to previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/suggestions/suggest-remove-refs-4.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// run-rustfix
fn main() {
let foo = [1,2,3].iter();
for _i in foo {} //~ ERROR E0277
}
5 changes: 5 additions & 0 deletions tests/ui/suggestions/suggest-remove-refs-4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// run-rustfix
fn main() {
let foo = &[1,2,3].iter();
for _i in &foo {} //~ ERROR E0277
}
17 changes: 17 additions & 0 deletions tests/ui/suggestions/suggest-remove-refs-4.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0277]: `&&std::slice::Iter<'_, {integer}>` is not an iterator
--> $DIR/suggest-remove-refs-4.rs:4:15
|
LL | for _i in &foo {}
| ^^^^ `&&std::slice::Iter<'_, {integer}>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&&std::slice::Iter<'_, {integer}>`
= note: required for `&&std::slice::Iter<'_, {integer}>` to implement `IntoIterator`
help: consider removing 2 leading `&`-references
|
LL ~ let foo = [1,2,3].iter();
LL ~ for _i in foo {}
|

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
8 changes: 8 additions & 0 deletions tests/ui/suggestions/suggest-remove-refs-5.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// run-rustfix
fn main() {
let v = &mut Vec::<i32>::new();
for _ in v {} //~ ERROR E0277

let v = &mut [1u8];
for _ in v {} //~ ERROR E0277
}
Loading