Skip to content

Commit d18dc3d

Browse files
authored
Rollup merge of #101367 - compiler-errors:suggest-copied-or-cloned, r=lcnr
Suggest `{Option,Result}::{copied,clone}()` to satisfy type mismatch Fixes #100699, but in the opposite direction (instead of suggesting to fix the signature, it fixes the body)
2 parents 5d55009 + fdbede7 commit d18dc3d

File tree

5 files changed

+194
-0
lines changed

5 files changed

+194
-0
lines changed

compiler/rustc_typeck/src/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4242
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
4343
self.suggest_missing_parentheses(err, expr);
4444
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
45+
self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
4546
self.note_type_is_not_clone(err, expected, expr_ty, expr);
4647
self.note_need_for_fn_pointer(err, expected, expr_ty);
4748
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+64
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_middle::lint::in_external_macro;
1717
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
1818
use rustc_span::symbol::sym;
1919
use rustc_span::Span;
20+
use rustc_trait_selection::infer::InferCtxtExt;
2021
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2122

2223
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -925,6 +926,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
925926
}
926927
}
927928

929+
pub(crate) fn suggest_copied_or_cloned(
930+
&self,
931+
diag: &mut Diagnostic,
932+
expr: &hir::Expr<'_>,
933+
expr_ty: Ty<'tcx>,
934+
expected_ty: Ty<'tcx>,
935+
) {
936+
let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
937+
let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
938+
if adt_def != expected_adt_def {
939+
return;
940+
}
941+
942+
let mut suggest_copied_or_cloned = || {
943+
let expr_inner_ty = substs.type_at(0);
944+
let expected_inner_ty = expected_substs.type_at(0);
945+
if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
946+
&& self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok()
947+
{
948+
let def_path = self.tcx.def_path_str(adt_def.did());
949+
if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
950+
diag.span_suggestion_verbose(
951+
expr.span.shrink_to_hi(),
952+
format!(
953+
"use `{def_path}::copied` to copy the value inside the `{def_path}`"
954+
),
955+
".copied()",
956+
Applicability::MachineApplicable,
957+
);
958+
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
959+
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
960+
self,
961+
self.param_env,
962+
*ty,
963+
clone_did,
964+
expr.span
965+
)
966+
{
967+
diag.span_suggestion_verbose(
968+
expr.span.shrink_to_hi(),
969+
format!(
970+
"use `{def_path}::cloned` to clone the value inside the `{def_path}`"
971+
),
972+
".cloned()",
973+
Applicability::MachineApplicable,
974+
);
975+
}
976+
}
977+
};
978+
979+
if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
980+
&& adt_def.did() == result_did
981+
// Check that the error types are equal
982+
&& self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
983+
{
984+
suggest_copied_or_cloned();
985+
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
986+
&& adt_def.did() == option_did
987+
{
988+
suggest_copied_or_cloned();
989+
}
990+
}
991+
928992
/// Suggest wrapping the block in square brackets instead of curly braces
929993
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
930994
pub(crate) fn suggest_block_to_brackets(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-rustfix
2+
3+
fn expect<T>(_: T) {}
4+
5+
fn main() {
6+
let x = Some(&());
7+
expect::<Option<()>>(x.copied());
8+
//~^ ERROR mismatched types
9+
//~| HELP use `Option::copied` to copy the value inside the `Option`
10+
let x = Ok(&());
11+
expect::<Result<(), ()>>(x.copied());
12+
//~^ ERROR mismatched types
13+
//~| HELP use `Result::copied` to copy the value inside the `Result`
14+
let s = String::new();
15+
let x = Some(&s);
16+
expect::<Option<String>>(x.cloned());
17+
//~^ ERROR mismatched types
18+
//~| HELP use `Option::cloned` to clone the value inside the `Option`
19+
let x = Ok(&s);
20+
expect::<Result<String, ()>>(x.cloned());
21+
//~^ ERROR mismatched types
22+
//~| HELP use `Result::cloned` to clone the value inside the `Result`
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-rustfix
2+
3+
fn expect<T>(_: T) {}
4+
5+
fn main() {
6+
let x = Some(&());
7+
expect::<Option<()>>(x);
8+
//~^ ERROR mismatched types
9+
//~| HELP use `Option::copied` to copy the value inside the `Option`
10+
let x = Ok(&());
11+
expect::<Result<(), ()>>(x);
12+
//~^ ERROR mismatched types
13+
//~| HELP use `Result::copied` to copy the value inside the `Result`
14+
let s = String::new();
15+
let x = Some(&s);
16+
expect::<Option<String>>(x);
17+
//~^ ERROR mismatched types
18+
//~| HELP use `Option::cloned` to clone the value inside the `Option`
19+
let x = Ok(&s);
20+
expect::<Result<String, ()>>(x);
21+
//~^ ERROR mismatched types
22+
//~| HELP use `Result::cloned` to clone the value inside the `Result`
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/copied-and-cloned.rs:7:26
3+
|
4+
LL | expect::<Option<()>>(x);
5+
| -------------------- ^ expected `()`, found `&()`
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
= note: expected enum `Option<()>`
10+
found enum `Option<&()>`
11+
note: function defined here
12+
--> $DIR/copied-and-cloned.rs:3:4
13+
|
14+
LL | fn expect<T>(_: T) {}
15+
| ^^^^^^ ----
16+
help: use `Option::copied` to copy the value inside the `Option`
17+
|
18+
LL | expect::<Option<()>>(x.copied());
19+
| +++++++++
20+
21+
error[E0308]: mismatched types
22+
--> $DIR/copied-and-cloned.rs:11:30
23+
|
24+
LL | expect::<Result<(), ()>>(x);
25+
| ------------------------ ^ expected `()`, found `&()`
26+
| |
27+
| arguments to this function are incorrect
28+
|
29+
= note: expected enum `Result<(), ()>`
30+
found enum `Result<&(), _>`
31+
note: function defined here
32+
--> $DIR/copied-and-cloned.rs:3:4
33+
|
34+
LL | fn expect<T>(_: T) {}
35+
| ^^^^^^ ----
36+
help: use `Result::copied` to copy the value inside the `Result`
37+
|
38+
LL | expect::<Result<(), ()>>(x.copied());
39+
| +++++++++
40+
41+
error[E0308]: mismatched types
42+
--> $DIR/copied-and-cloned.rs:16:30
43+
|
44+
LL | expect::<Option<String>>(x);
45+
| ------------------------ ^ expected struct `String`, found `&String`
46+
| |
47+
| arguments to this function are incorrect
48+
|
49+
= note: expected enum `Option<String>`
50+
found enum `Option<&String>`
51+
note: function defined here
52+
--> $DIR/copied-and-cloned.rs:3:4
53+
|
54+
LL | fn expect<T>(_: T) {}
55+
| ^^^^^^ ----
56+
help: use `Option::cloned` to clone the value inside the `Option`
57+
|
58+
LL | expect::<Option<String>>(x.cloned());
59+
| +++++++++
60+
61+
error[E0308]: mismatched types
62+
--> $DIR/copied-and-cloned.rs:20:34
63+
|
64+
LL | expect::<Result<String, ()>>(x);
65+
| ---------------------------- ^ expected struct `String`, found `&String`
66+
| |
67+
| arguments to this function are incorrect
68+
|
69+
= note: expected enum `Result<String, ()>`
70+
found enum `Result<&String, _>`
71+
note: function defined here
72+
--> $DIR/copied-and-cloned.rs:3:4
73+
|
74+
LL | fn expect<T>(_: T) {}
75+
| ^^^^^^ ----
76+
help: use `Result::cloned` to clone the value inside the `Result`
77+
|
78+
LL | expect::<Result<String, ()>>(x.cloned());
79+
| +++++++++
80+
81+
error: aborting due to 4 previous errors
82+
83+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)