diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 506e2822a8745..573c08895255b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2972,8 +2972,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. let expr = expr.peel_drop_temps(); - match (&expr.kind, expected.kind(), checked_ty.kind()) { + // Handle call arguments that need another shared or mutable reference, such as + // `&T` to `&&T` or `&T` to `&mut &T`. + // Keep ordinary `T` to `&T` cases on later path so its more + // specific suggestions, such as `Option::as_ref()`, are preserved. + (_, &ty::Ref(_, exp, mutability), _) + if exp.is_ref() + && matches!( + self.tcx.parent_hir_node(expr.hir_id), + hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Call(_, args) + | hir::ExprKind::MethodCall(_, _, args, _), + .. + }) if args.iter().any(|arg| arg.hir_id == expr.hir_id) + ) + && self.can_eq(self.param_env, exp, checked_ty) => + { + let borrow = mutability.ref_prefix_str(); + let sugg = if expr_needs_parens(expr) { + vec![ + (sp.shrink_to_lo(), format!("{borrow}(")), + (sp.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(sp.shrink_to_lo(), borrow.to_string())] + }; + return Some(( + sugg, + format!("consider {}borrowing here", mutability.mutably_str()), + Applicability::MachineApplicable, + false, + )); + } (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) { (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind diff --git a/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.fixed b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.fixed new file mode 100644 index 0000000000000..1e414476c34a1 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.fixed @@ -0,0 +1,44 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/78613. +//! A call argument that needs one more reference should suggest borrowing it. + +//@ run-rustfix + +fn takes_nested_ref(_: &&str) {} + +fn takes_generic_nested_ref(_: &&T) {} + +fn takes_nested_mut_ref(_: &mut &str) {} + +fn takes_ref(_: &i32) {} + +struct Foo; + +fn takes_foo_ref(_: &Foo) {} + +fn main() { + let haystack = [&["A1", "A2"][..], &["B1", "B2"], &["C1", "C2"]]; + let needle: &[&str] = &["D1", "D2"]; + let _ = haystack.contains(&needle); + //~^ ERROR mismatched types + + let text = "text"; + takes_nested_ref(&text); + //~^ ERROR mismatched types + + let number = &1; + takes_generic_nested_ref(&number); + //~^ ERROR mismatched types + + let mut mutable_text = text; + takes_nested_mut_ref(&mut mutable_text); + //~^ ERROR mismatched types + + takes_ref(if true { &1 } else { &2 }); + //~^ ERROR mismatched types + //~| ERROR mismatched types + + // Ordinary `T` to `&T` cases should retain more specific suggestions from the existing path. + let ref opt = Some(Foo); + opt.as_ref().map(|arg| takes_foo_ref(arg)); + //~^ ERROR mismatched types +} diff --git a/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.rs b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.rs new file mode 100644 index 0000000000000..98cffb0aeabc8 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.rs @@ -0,0 +1,44 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/78613. +//! A call argument that needs one more reference should suggest borrowing it. + +//@ run-rustfix + +fn takes_nested_ref(_: &&str) {} + +fn takes_generic_nested_ref(_: &&T) {} + +fn takes_nested_mut_ref(_: &mut &str) {} + +fn takes_ref(_: &i32) {} + +struct Foo; + +fn takes_foo_ref(_: &Foo) {} + +fn main() { + let haystack = [&["A1", "A2"][..], &["B1", "B2"], &["C1", "C2"]]; + let needle: &[&str] = &["D1", "D2"]; + let _ = haystack.contains(needle); + //~^ ERROR mismatched types + + let text = "text"; + takes_nested_ref(text); + //~^ ERROR mismatched types + + let number = &1; + takes_generic_nested_ref(number); + //~^ ERROR mismatched types + + let mut mutable_text = text; + takes_nested_mut_ref(mutable_text); + //~^ ERROR mismatched types + + takes_ref(if true { 1 } else { 2 }); + //~^ ERROR mismatched types + //~| ERROR mismatched types + + // Ordinary `T` to `&T` cases should retain more specific suggestions from the existing path. + let ref opt = Some(Foo); + opt.map(|arg| takes_foo_ref(arg)); + //~^ ERROR mismatched types +} diff --git a/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.stderr b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.stderr new file mode 100644 index 0000000000000..f0d0deff599db --- /dev/null +++ b/tests/ui/mismatched_types/suggest-extra-borrow-issue-78613.stderr @@ -0,0 +1,120 @@ +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:21:31 + | +LL | let _ = haystack.contains(needle); + | -------- ^^^^^^ expected `&&[&str]`, found `&[&str]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&&[&str]` + found reference `&[&str]` +note: method defined here + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: consider borrowing here + | +LL | let _ = haystack.contains(&needle); + | + + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:25:22 + | +LL | takes_nested_ref(text); + | ---------------- ^^^^ expected `&&str`, found `&str` + | | + | arguments to this function are incorrect + | + = note: expected reference `&&_` + found reference `&_` +note: function defined here + --> $DIR/suggest-extra-borrow-issue-78613.rs:6:4 + | +LL | fn takes_nested_ref(_: &&str) {} + | ^^^^^^^^^^^^^^^^ -------- +help: consider borrowing here + | +LL | takes_nested_ref(&text); + | + + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:29:30 + | +LL | takes_generic_nested_ref(number); + | ------------------------ ^^^^^^ expected `&&_`, found `&{integer}` + | | + | arguments to this function are incorrect + | + = note: expected reference `&&_` + found reference `&{integer}` +note: function defined here + --> $DIR/suggest-extra-borrow-issue-78613.rs:8:4 + | +LL | fn takes_generic_nested_ref(_: &&T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ ------ +help: consider borrowing here + | +LL | takes_generic_nested_ref(&number); + | + + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:33:26 + | +LL | takes_nested_mut_ref(mutable_text); + | -------------------- ^^^^^^^^^^^^ types differ in mutability + | | + | arguments to this function are incorrect + | + = note: expected mutable reference `&mut &_` + found reference `&_` +note: function defined here + --> $DIR/suggest-extra-borrow-issue-78613.rs:10:4 + | +LL | fn takes_nested_mut_ref(_: &mut &str) {} + | ^^^^^^^^^^^^^^^^^^^^ ------------ +help: consider mutably borrowing here + | +LL | takes_nested_mut_ref(&mut mutable_text); + | ++++ + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:36:25 + | +LL | takes_ref(if true { 1 } else { 2 }); + | ^ expected `&i32`, found integer + | +help: consider borrowing here + | +LL | takes_ref(if true { &1 } else { 2 }); + | + + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:36:36 + | +LL | takes_ref(if true { 1 } else { 2 }); + | ^ expected `&i32`, found integer + | +help: consider borrowing here + | +LL | takes_ref(if true { 1 } else { &2 }); + | + + +error[E0308]: mismatched types + --> $DIR/suggest-extra-borrow-issue-78613.rs:42:33 + | +LL | opt.map(|arg| takes_foo_ref(arg)); + | ------------- ^^^ expected `&Foo`, found `Foo` + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/suggest-extra-borrow-issue-78613.rs:16:4 + | +LL | fn takes_foo_ref(_: &Foo) {} + | ^^^^^^^^^^^^^ ------- +help: consider using `as_ref` instead + | +LL | opt.as_ref().map(|arg| takes_foo_ref(arg)); + | +++++++++ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`.