From 1eb828ecb1675073c7995db80be2e63719fd73c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 1 Jan 2023 21:28:03 -0800 Subject: [PATCH 1/4] Structured suggestion for `&mut dyn Iterator` when possible Fix #37914. --- compiler/rustc_hir_typeck/src/method/mod.rs | 4 +-- .../rustc_hir_typeck/src/method/suggest.rs | 31 +++++++++++++++++-- .../mutability-mismatch-arg.fixed | 9 ++++++ .../mutability-mismatch-arg.rs | 9 ++++++ .../mutability-mismatch-arg.stderr | 16 ++++++++++ .../mutability-mismatch.rs | 4 +-- .../mutability-mismatch.stderr | 4 +-- .../suggestions/imm-ref-trait-object.stderr | 5 ++- 8 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed create mode 100644 src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs create mode 100644 src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index b9b27e8627a..f3c43e3f497 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -57,7 +57,7 @@ pub enum MethodError<'tcx> { PrivateMatch(DefKind, DefId, Vec), // Found a `Self: Sized` bound where `Self` is a trait object. - IllegalSizedBound(Vec, bool, Span), + IllegalSizedBound(Vec, bool, Span, &'tcx hir::Expr<'tcx>), // Found a match, but the return type is wrong BadReturnType, @@ -236,7 +236,7 @@ pub fn lookup_method( _ => Vec::new(), }; - return Err(IllegalSizedBound(candidates, needs_mut, span)); + return Err(IllegalSizedBound(candidates, needs_mut, span, self_expr)); } Ok(result.callee) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 1a42f9d07b1..5a43db69fd4 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -176,7 +176,7 @@ pub fn report_method_error( err.emit(); } - MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { + MethodError::IllegalSizedBound(candidates, needs_mut, bound_span, self_expr) => { let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); let mut err = self.sess().struct_span_err(span, &msg); err.span_label(bound_span, "this has a `Sized` requirement"); @@ -197,7 +197,34 @@ pub fn report_method_error( *region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, ); - err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); + let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty); + let mut kind = &self_expr.kind; + while let hir::ExprKind::AddrOf(_, _, expr) + | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind + { + kind = &expr.kind; + } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind + && let hir::def::Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) + && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) + && let Some(hir::Node::Param(param)) = self.tcx.hir().find(parent_hir_id) + && let parent_hir_id = self.tcx.hir().get_parent_node(param.hir_id) + && let Some(node) = self.tcx.hir().find(parent_hir_id) + && let Some(decl) = node.fn_decl() + && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == param.ty_span) + && let hir::TyKind::Ref(_, mut_ty) = &ty.kind + && let hir::Mutability::Not = mut_ty.mutbl + { + err.span_suggestion_verbose( + mut_ty.ty.span.shrink_to_lo(), + &msg, + "mut ", + Applicability::MachineApplicable, + ); + } else { + err.help(&msg); + } } } err.emit(); diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed new file mode 100644 index 00000000000..260ef5458d4 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed @@ -0,0 +1,9 @@ +// run-rustfix +fn test(t: &mut dyn Iterator) -> u64 { + *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object +} + +fn main() { + let array = [0u64]; + test(&mut array.iter()); +} diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs new file mode 100644 index 00000000000..7a1656507f2 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs @@ -0,0 +1,9 @@ +// run-rustfix +fn test(t: &dyn Iterator) -> u64 { + *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object +} + +fn main() { + let array = [0u64]; + test(&mut array.iter()); +} diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr new file mode 100644 index 00000000000..9b4b9b65d10 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr @@ -0,0 +1,16 @@ +error: the `min` method cannot be invoked on a trait object + --> $DIR/mutability-mismatch-arg.rs:3:9 + | +LL | *t.min().unwrap() + | ^^^ + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + | + = note: this has a `Sized` requirement + | +help: you need `&mut dyn Iterator` instead of `&dyn Iterator` + | +LL | fn test(t: &mut dyn Iterator) -> u64 { + | +++ + +error: aborting due to previous error + diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.rs b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs index deb84f6fe97..aa9b3d03891 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch.rs +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs @@ -27,8 +27,8 @@ fn function(&self) {} fn main() { (&MutType as &dyn MutTrait).function(); //~^ ERROR the `function` method cannot be invoked on a trait object - //~| NOTE you need `&mut dyn MutTrait` instead of `&dyn MutTrait` + //~| HELP you need `&mut dyn MutTrait` instead of `&dyn MutTrait` (&mut Type as &mut dyn Trait).function(); //~^ ERROR the `function` method cannot be invoked on a trait object - //~| NOTE you need `&dyn Trait` instead of `&mut dyn Trait` + //~| HELP you need `&dyn Trait` instead of `&mut dyn Trait` } diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr index dbbf79a4f1a..0120b9f91e9 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr @@ -7,7 +7,7 @@ LL | Self: Sized; LL | (&MutType as &dyn MutTrait).function(); | ^^^^^^^^ | - = note: you need `&mut dyn MutTrait` instead of `&dyn MutTrait` + = help: you need `&mut dyn MutTrait` instead of `&dyn MutTrait` error: the `function` method cannot be invoked on a trait object --> $DIR/mutability-mismatch.rs:31:35 @@ -18,7 +18,7 @@ LL | Self: Sized; LL | (&mut Type as &mut dyn Trait).function(); | ^^^^^^^^ | - = note: you need `&dyn Trait` instead of `&mut dyn Trait` + = help: you need `&dyn Trait` instead of `&mut dyn Trait` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr index 7791b308d5d..02847ed8c4c 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -7,7 +7,10 @@ LL | t.min().unwrap() | = note: this has a `Sized` requirement | - = note: you need `&mut dyn Iterator` instead of `&dyn Iterator` +help: you need `&mut dyn Iterator` instead of `&dyn Iterator` + | +LL | fn test(t: &mut dyn Iterator) -> u64 { + | +++ error: aborting due to previous error From 670a6f1ef5d895d8b9ef5bba6f71576a4428b47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 2 Jan 2023 09:55:13 -0800 Subject: [PATCH 2/4] Change wording to avoid being misleading --- compiler/rustc_hir_typeck/src/method/suggest.rs | 12 ++++++++++-- .../mutability-mismatch-arg.fixed | 2 +- .../illegal-sized-bound/mutability-mismatch-arg.rs | 2 +- .../mutability-mismatch-arg.stderr | 5 +---- .../ui/illegal-sized-bound/mutability-mismatch.rs | 6 ++---- .../illegal-sized-bound/mutability-mismatch.stderr | 14 ++++---------- src/test/ui/suggestions/imm-ref-trait-object.rs | 2 +- .../ui/suggestions/imm-ref-trait-object.stderr | 5 +---- 8 files changed, 21 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 5a43db69fd4..fedffe3d81e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -177,9 +177,17 @@ pub fn report_method_error( } MethodError::IllegalSizedBound(candidates, needs_mut, bound_span, self_expr) => { - let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); + let msg = if needs_mut { + with_forced_trimmed_paths!(format!( + "the `{item_name}` method cannot be invoked on `{rcvr_ty}`" + )) + } else { + format!("the `{item_name}` method cannot be invoked on a trait object") + }; let mut err = self.sess().struct_span_err(span, &msg); - err.span_label(bound_span, "this has a `Sized` requirement"); + if !needs_mut { + err.span_label(bound_span, "this has a `Sized` requirement"); + } if !candidates.is_empty() { let help = format!( "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed index 260ef5458d4..74f3c887f02 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed @@ -1,6 +1,6 @@ // run-rustfix fn test(t: &mut dyn Iterator) -> u64 { - *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object + *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on } fn main() { diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs index 7a1656507f2..3b02c5a5ad1 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs @@ -1,6 +1,6 @@ // run-rustfix fn test(t: &dyn Iterator) -> u64 { - *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object + *t.min().unwrap() //~ ERROR the `min` method cannot be invoked on } fn main() { diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr index 9b4b9b65d10..89613bd5c20 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr @@ -1,11 +1,8 @@ -error: the `min` method cannot be invoked on a trait object +error: the `min` method cannot be invoked on `&dyn Iterator` --> $DIR/mutability-mismatch-arg.rs:3:9 | LL | *t.min().unwrap() | ^^^ - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this has a `Sized` requirement | help: you need `&mut dyn Iterator` instead of `&dyn Iterator` | diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.rs b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs index aa9b3d03891..01bb3537c2d 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch.rs +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs @@ -4,7 +4,6 @@ pub trait MutTrait { fn function(&mut self) where Self: Sized; - //~^ this has a `Sized` requirement } impl MutTrait for MutType { @@ -17,7 +16,6 @@ pub trait Trait { fn function(&self) where Self: Sized; - //~^ this has a `Sized` requirement } impl Trait for Type { @@ -26,9 +24,9 @@ fn function(&self) {} fn main() { (&MutType as &dyn MutTrait).function(); - //~^ ERROR the `function` method cannot be invoked on a trait object + //~^ ERROR the `function` method cannot be invoked on `&dyn MutTrait` //~| HELP you need `&mut dyn MutTrait` instead of `&dyn MutTrait` (&mut Type as &mut dyn Trait).function(); - //~^ ERROR the `function` method cannot be invoked on a trait object + //~^ ERROR the `function` method cannot be invoked on `&mut dyn Trait` //~| HELP you need `&dyn Trait` instead of `&mut dyn Trait` } diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr index 0120b9f91e9..2ca571d9b79 100644 --- a/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr @@ -1,20 +1,14 @@ -error: the `function` method cannot be invoked on a trait object - --> $DIR/mutability-mismatch.rs:28:33 +error: the `function` method cannot be invoked on `&dyn MutTrait` + --> $DIR/mutability-mismatch.rs:26:33 | -LL | Self: Sized; - | ----- this has a `Sized` requirement -... LL | (&MutType as &dyn MutTrait).function(); | ^^^^^^^^ | = help: you need `&mut dyn MutTrait` instead of `&dyn MutTrait` -error: the `function` method cannot be invoked on a trait object - --> $DIR/mutability-mismatch.rs:31:35 +error: the `function` method cannot be invoked on `&mut dyn Trait` + --> $DIR/mutability-mismatch.rs:29:35 | -LL | Self: Sized; - | ----- this has a `Sized` requirement -... LL | (&mut Type as &mut dyn Trait).function(); | ^^^^^^^^ | diff --git a/src/test/ui/suggestions/imm-ref-trait-object.rs b/src/test/ui/suggestions/imm-ref-trait-object.rs index 288d6c699f5..c1c969b90e4 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.rs +++ b/src/test/ui/suggestions/imm-ref-trait-object.rs @@ -1,5 +1,5 @@ fn test(t: &dyn Iterator) -> u64 { - t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object + t.min().unwrap() //~ ERROR the `min` method cannot be invoked on `&dyn Iterator` } fn main() { diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr index 02847ed8c4c..f7f7902c17d 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -1,11 +1,8 @@ -error: the `min` method cannot be invoked on a trait object +error: the `min` method cannot be invoked on `&dyn Iterator` --> $DIR/imm-ref-trait-object.rs:2:8 | LL | t.min().unwrap() | ^^^ - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this has a `Sized` requirement | help: you need `&mut dyn Iterator` instead of `&dyn Iterator` | From 2631a5df6135b8cf877fa2464fe428a6bc704048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 07:12:04 +0000 Subject: [PATCH 3/4] Turn `IllegalSizedBound` into struct variant --- compiler/rustc_hir_typeck/src/method/mod.rs | 11 ++++++++--- compiler/rustc_hir_typeck/src/method/suggest.rs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index f3c43e3f497..d276bcdb81e 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -57,7 +57,12 @@ pub enum MethodError<'tcx> { PrivateMatch(DefKind, DefId, Vec), // Found a `Self: Sized` bound where `Self` is a trait object. - IllegalSizedBound(Vec, bool, Span, &'tcx hir::Expr<'tcx>), + IllegalSizedBound { + candidates: Vec, + needs_mut: bool, + bound_span: Span, + self_expr: &'tcx hir::Expr<'tcx>, + }, // Found a match, but the return type is wrong BadReturnType, @@ -112,7 +117,7 @@ pub fn method_exists( Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, - Err(IllegalSizedBound(..)) => true, + Err(IllegalSizedBound { .. }) => true, Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), } } @@ -236,7 +241,7 @@ pub fn lookup_method( _ => Vec::new(), }; - return Err(IllegalSizedBound(candidates, needs_mut, span, self_expr)); + return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr }); } Ok(result.callee) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index fedffe3d81e..bcdb557be21 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -176,7 +176,7 @@ pub fn report_method_error( err.emit(); } - MethodError::IllegalSizedBound(candidates, needs_mut, bound_span, self_expr) => { + MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { let msg = if needs_mut { with_forced_trimmed_paths!(format!( "the `{item_name}` method cannot be invoked on `{rcvr_ty}`" From b693365b846b13c1da6c5158cc7f4598a1aaa2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Jan 2023 21:08:56 +0000 Subject: [PATCH 4/4] fix rebase --- compiler/rustc_hir_typeck/src/method/suggest.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index bcdb557be21..536c4270659 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -214,13 +214,11 @@ pub fn report_method_error( } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind && let hir::def::Res::Local(hir_id) = path.res - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) - && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) - && let Some(hir::Node::Param(param)) = self.tcx.hir().find(parent_hir_id) - && let parent_hir_id = self.tcx.hir().get_parent_node(param.hir_id) - && let Some(node) = self.tcx.hir().find(parent_hir_id) + && let Some(hir::Node::Pat(b)) = self.tcx.hir().find(hir_id) + && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id) + && let Some(node) = self.tcx.hir().find_parent(p.hir_id) && let Some(decl) = node.fn_decl() - && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == param.ty_span) + && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) && let hir::TyKind::Ref(_, mut_ty) = &ty.kind && let hir::Mutability::Not = mut_ty.mutbl {