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

Structured suggestion for &mut dyn Iterator when possible #106363

Merged
merged 4 commits into from
Jan 9, 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
11 changes: 8 additions & 3 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ pub enum MethodError<'tcx> {
PrivateMatch(DefKind, DefId, Vec<DefId>),

// Found a `Self: Sized` bound where `Self` is a trait object.
IllegalSizedBound(Vec<DefId>, bool, Span),
IllegalSizedBound {
candidates: Vec<DefId>,
needs_mut: bool,
bound_span: Span,
self_expr: &'tcx hir::Expr<'tcx>,
},

// Found a match, but the return type is wrong
BadReturnType,
Expand Down Expand Up @@ -112,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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"),
}
}
Expand Down Expand Up @@ -236,7 +241,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => Vec::new(),
};

return Err(IllegalSizedBound(candidates, needs_mut, span));
return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
}

Ok(result.callee)
Expand Down
41 changes: 37 additions & 4 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}

MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => {
let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
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}`"
))
} 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 \
Expand All @@ -197,7 +205,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
*region,
ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
Noratrieb marked this conversation as resolved.
Show resolved Hide resolved
);
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(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 == p.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();
Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/illegal-sized-bound/mutability-mismatch-arg.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// run-rustfix
fn test(t: &mut dyn Iterator<Item=&u64>) -> u64 {
*t.min().unwrap() //~ ERROR the `min` method cannot be invoked on
}

fn main() {
let array = [0u64];
test(&mut array.iter());
}
9 changes: 9 additions & 0 deletions src/test/ui/illegal-sized-bound/mutability-mismatch-arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// run-rustfix
fn test(t: &dyn Iterator<Item=&u64>) -> u64 {
*t.min().unwrap() //~ ERROR the `min` method cannot be invoked on
}

fn main() {
let array = [0u64];
test(&mut array.iter());
}
13 changes: 13 additions & 0 deletions src/test/ui/illegal-sized-bound/mutability-mismatch-arg.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: the `min` method cannot be invoked on `&dyn Iterator<Item = &u64>`
--> $DIR/mutability-mismatch-arg.rs:3:9
|
LL | *t.min().unwrap()
| ^^^
|
help: you need `&mut dyn Iterator<Item = &u64>` instead of `&dyn Iterator<Item = &u64>`
|
LL | fn test(t: &mut dyn Iterator<Item=&u64>) -> u64 {
| +++

error: aborting due to previous error

10 changes: 4 additions & 6 deletions src/test/ui/illegal-sized-bound/mutability-mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub trait MutTrait {
fn function(&mut self)
where
Self: Sized;
//~^ this has a `Sized` requirement
}

impl MutTrait for MutType {
Expand All @@ -17,7 +16,6 @@ pub trait Trait {
fn function(&self)
where
Self: Sized;
//~^ this has a `Sized` requirement
}

impl Trait for Type {
Expand All @@ -26,9 +24,9 @@ impl Trait for Type {

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`
//~^ 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
//~| NOTE you need `&dyn Trait` instead of `&mut dyn Trait`
//~^ ERROR the `function` method cannot be invoked on `&mut dyn Trait`
//~| HELP you need `&dyn Trait` instead of `&mut dyn Trait`
}
18 changes: 6 additions & 12 deletions src/test/ui/illegal-sized-bound/mutability-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
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();
| ^^^^^^^^
|
= 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
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();
| ^^^^^^^^
|
= 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

2 changes: 1 addition & 1 deletion src/test/ui/suggestions/imm-ref-trait-object.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
fn test(t: &dyn Iterator<Item=&u64>) -> 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<Item = &u64>`
}

fn main() {
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/suggestions/imm-ref-trait-object.stderr
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
error: the `min` method cannot be invoked on a trait object
error: the `min` method cannot be invoked on `&dyn Iterator<Item = &u64>`
--> $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<Item = &u64>` instead of `&dyn Iterator<Item = &u64>`
|
= note: you need `&mut dyn Iterator<Item = &u64>` instead of `&dyn Iterator<Item = &u64>`
LL | fn test(t: &mut dyn Iterator<Item=&u64>) -> u64 {
| +++

error: aborting due to previous error