Skip to content

Commit f9f674f

Browse files
committed
Auto merge of #114150 - clubby789:improve-option-ref-suggestion, r=WaffleLapkin
Refactor + improve diagnostics for `&mut T`/`T` mismatch inside Option/Result Follow up to #114052. This also makes the diagnostics structured + translatable. r? `@WaffleLapkin`
2 parents f45961b + fafc3d2 commit f9f674f

File tree

7 files changed

+153
-74
lines changed

7 files changed

+153
-74
lines changed

Diff for: compiler/rustc_hir_typeck/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
7777
7878
hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
7979
80+
hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}`
81+
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
82+
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
83+
8084
hir_typeck_return_stmt_outside_of_fn_body =
8185
{$statement_kind} statement outside of function body
8286
.encl_body_label = the {$statement_kind} is part of this body...

Diff for: compiler/rustc_hir_typeck/src/errors.rs

+39
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,45 @@ impl HelpUseLatestEdition {
252252
}
253253
}
254254

255+
#[derive(Subdiagnostic)]
256+
pub enum OptionResultRefMismatch<'tcx> {
257+
#[suggestion(
258+
hir_typeck_option_result_copied,
259+
code = ".copied()",
260+
style = "verbose",
261+
applicability = "machine-applicable"
262+
)]
263+
Copied {
264+
#[primary_span]
265+
span: Span,
266+
def_path: String,
267+
},
268+
#[suggestion(
269+
hir_typeck_option_result_cloned,
270+
code = ".cloned()",
271+
style = "verbose",
272+
applicability = "machine-applicable"
273+
)]
274+
Cloned {
275+
#[primary_span]
276+
span: Span,
277+
def_path: String,
278+
},
279+
#[suggestion(
280+
hir_typeck_option_result_asref,
281+
code = ".as_ref()",
282+
style = "verbose",
283+
applicability = "machine-applicable"
284+
)]
285+
AsRef {
286+
#[primary_span]
287+
span: Span,
288+
def_path: String,
289+
expected_ty: Ty<'tcx>,
290+
expr_ty: Ty<'tcx>,
291+
},
292+
}
293+
255294
#[derive(Diagnostic)]
256295
#[diag(hir_typeck_const_select_must_be_const)]
257296
#[help]

Diff for: compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+51-69
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use super::FnCtxt;
22

3-
use crate::errors::{
4-
AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
5-
};
3+
use crate::errors;
64
use crate::fluent_generated as fluent;
75
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
86
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
@@ -434,7 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
434432
// FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
435433
// but those checks need to be a bit more delicate and the benefit is diminishing.
436434
if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
437-
err.subdiagnostic(SuggestConvertViaMethod {
435+
err.subdiagnostic(errors::SuggestConvertViaMethod {
438436
span: expr.span.shrink_to_hi(),
439437
sugg: ".as_ref()",
440438
expected,
@@ -447,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
447445
&& self.can_eq(self.param_env, deref_ty, peeled)
448446
&& error_tys_equate_as_ref
449447
{
450-
err.subdiagnostic(SuggestConvertViaMethod {
448+
err.subdiagnostic(errors::SuggestConvertViaMethod {
451449
span: expr.span.shrink_to_hi(),
452450
sugg: ".as_deref()",
453451
expected,
@@ -521,17 +519,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
521519
if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
522520
let suggest_boxing = match found.kind() {
523521
ty::Tuple(tuple) if tuple.is_empty() => {
524-
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
522+
errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
525523
}
526524
ty::Generator(def_id, ..)
527525
if matches!(
528526
self.tcx.generator_kind(def_id),
529527
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
530528
) =>
531529
{
532-
SuggestBoxing::AsyncBody
530+
errors::SuggestBoxing::AsyncBody
533531
}
534-
_ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() },
532+
_ => errors::SuggestBoxing::Other {
533+
start: span.shrink_to_lo(),
534+
end: span.shrink_to_hi(),
535+
},
535536
};
536537
err.subdiagnostic(suggest_boxing);
537538

@@ -756,23 +757,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
756757
match &fn_decl.output {
757758
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
758759
// `fn main()` must return `()`, do not suggest changing return type
759-
err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
760+
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
760761
return true;
761762
}
762763
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
763764
if let Some(found) = found.make_suggestable(self.tcx, false) {
764-
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
765+
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
765766
return true;
766767
} else if let ty::Closure(_, args) = found.kind()
767768
// FIXME(compiler-errors): Get better at printing binders...
768769
&& let closure = args.as_closure()
769770
&& closure.sig().is_suggestable(self.tcx, false)
770771
{
771-
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
772+
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
772773
return true;
773774
} else {
774775
// FIXME: if `found` could be `impl Iterator` we should suggest that.
775-
err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
776+
err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
776777
return true
777778
}
778779
}
@@ -794,10 +795,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
794795
debug!(?found);
795796
if found.is_suggestable(self.tcx, false) {
796797
if term.span.is_empty() {
797-
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
798+
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
798799
return true;
799800
} else {
800-
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
801+
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
801802
}
802803
}
803804
}
@@ -813,7 +814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
813814
let ty = self.normalize(span, ty);
814815
let ty = self.tcx.erase_late_bound_regions(ty);
815816
if self.can_coerce(expected, ty) {
816-
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
817+
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
817818
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
818819
return true;
819820
}
@@ -1103,65 +1104,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11031104
return false;
11041105
}
11051106

1106-
let mut suggest_copied_cloned_or_as_ref = || {
1107+
if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1108+
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1109+
|| Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1110+
{
11071111
let expr_inner_ty = args.type_at(0);
11081112
let expected_inner_ty = expected_args.type_at(0);
1109-
if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
1110-
&& self.can_eq(self.param_env, ty, expected_inner_ty)
1111-
{
1112-
let def_path = self.tcx.def_path_str(adt_def.did());
1113-
if self.type_is_copy_modulo_regions(self.param_env, ty) {
1114-
diag.span_suggestion_verbose(
1115-
expr.span.shrink_to_hi(),
1116-
format!(
1117-
"use `{def_path}::copied` to copy the value inside the `{def_path}`"
1118-
),
1119-
".copied()",
1120-
Applicability::MachineApplicable,
1121-
);
1122-
return true;
1123-
} else if let Some(expected_ty_expr) = expected_ty_expr {
1124-
diag.span_suggestion_verbose(
1125-
expected_ty_expr.span.shrink_to_hi(),
1126-
format!(
1127-
"use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
1128-
),
1129-
".as_ref()",
1130-
Applicability::MachineApplicable,
1131-
);
1132-
return true;
1133-
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
1134-
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
1135-
self,
1136-
self.param_env,
1137-
ty,
1138-
clone_did,
1139-
)
1113+
if let &ty::Ref(_, ty, mutability) = expr_inner_ty.kind()
1114+
&& self.can_eq(self.param_env, ty, expected_inner_ty)
11401115
{
1141-
diag.span_suggestion_verbose(
1142-
expr.span.shrink_to_hi(),
1143-
format!(
1144-
"use `{def_path}::cloned` to clone the value inside the `{def_path}`"
1145-
),
1146-
".cloned()",
1147-
Applicability::MachineApplicable,
1148-
);
1116+
let def_path = self.tcx.def_path_str(adt_def.did());
1117+
let span = expr.span.shrink_to_hi();
1118+
let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1119+
errors::OptionResultRefMismatch::Copied {
1120+
span, def_path
1121+
}
1122+
} else if let Some(expected_ty_expr) = expected_ty_expr
1123+
// FIXME: suggest changes to both expressions to convert both to
1124+
// Option/Result<&T>
1125+
&& mutability.is_not()
1126+
{
1127+
errors::OptionResultRefMismatch::AsRef {
1128+
span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path
1129+
}
1130+
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
1131+
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
1132+
self,
1133+
self.param_env,
1134+
ty,
1135+
clone_did,
1136+
)
1137+
{
1138+
errors::OptionResultRefMismatch::Cloned {
1139+
span, def_path
1140+
}
1141+
} else {
1142+
return false;
1143+
};
1144+
diag.subdiagnostic(subdiag);
11491145
return true;
11501146
}
1151-
}
1152-
false
1153-
};
1154-
1155-
if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
1156-
&& adt_def.did() == result_did
1157-
// Check that the error types are equal
1158-
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1159-
{
1160-
return suggest_copied_cloned_or_as_ref();
1161-
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
1162-
&& adt_def.did() == option_did
1163-
{
1164-
return suggest_copied_cloned_or_as_ref();
11651147
}
11661148

11671149
false

Diff for: tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | debug_assert_eq!(iter.next(), Some(value));
66
|
77
= note: expected enum `Option<<I as Iterator>::Item>`
88
found enum `Option<&<I as Iterator>::Item>`
9-
help: use `Option::as_ref()` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
9+
help: use `Option::as_ref` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
1010
|
1111
LL | debug_assert_eq!(iter.next().as_ref(), Some(value));
1212
| +++++++++

Diff for: tests/ui/suggestions/copied-and-cloned.fixed

+15-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ fn main() {
2626
let y = Some(&s);
2727
println!("{}", x.as_ref() == y);
2828
//~^ ERROR mismatched types
29-
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
29+
//~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
3030

31+
32+
let mut s = ();
33+
let x = Some(s);
34+
let y = Some(&mut s);
35+
println!("{}", x == y.copied());
36+
//~^ ERROR mismatched types
37+
//~| HELP use `Option::copied` to copy the value inside the `Option`
38+
39+
let mut s = String::new();
40+
let x = Some(s.clone());
41+
let y = Some(&mut s);
42+
println!("{}", x == y.cloned());
43+
//~^ ERROR mismatched types
44+
//~| HELP use `Option::cloned` to clone the value inside the `Option`
3145
}

Diff for: tests/ui/suggestions/copied-and-cloned.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ fn main() {
2626
let y = Some(&s);
2727
println!("{}", x == y);
2828
//~^ ERROR mismatched types
29-
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
29+
//~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
3030

31+
32+
let mut s = ();
33+
let x = Some(s);
34+
let y = Some(&mut s);
35+
println!("{}", x == y);
36+
//~^ ERROR mismatched types
37+
//~| HELP use `Option::copied` to copy the value inside the `Option`
38+
39+
let mut s = String::new();
40+
let x = Some(s.clone());
41+
let y = Some(&mut s);
42+
println!("{}", x == y);
43+
//~^ ERROR mismatched types
44+
//~| HELP use `Option::cloned` to clone the value inside the `Option`
3145
}

Diff for: tests/ui/suggestions/copied-and-cloned.stderr

+28-2
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,37 @@ LL | println!("{}", x == y);
8686
|
8787
= note: expected enum `Option<String>`
8888
found enum `Option<&String>`
89-
help: use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
89+
help: use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
9090
|
9191
LL | println!("{}", x.as_ref() == y);
9292
| +++++++++
9393

94-
error: aborting due to 5 previous errors
94+
error[E0308]: mismatched types
95+
--> $DIR/copied-and-cloned.rs:35:25
96+
|
97+
LL | println!("{}", x == y);
98+
| ^ expected `Option<()>`, found `Option<&mut ()>`
99+
|
100+
= note: expected enum `Option<()>`
101+
found enum `Option<&mut ()>`
102+
help: use `Option::copied` to copy the value inside the `Option`
103+
|
104+
LL | println!("{}", x == y.copied());
105+
| +++++++++
106+
107+
error[E0308]: mismatched types
108+
--> $DIR/copied-and-cloned.rs:42:25
109+
|
110+
LL | println!("{}", x == y);
111+
| ^ expected `Option<String>`, found `Option<&mut String>`
112+
|
113+
= note: expected enum `Option<String>`
114+
found enum `Option<&mut String>`
115+
help: use `Option::cloned` to clone the value inside the `Option`
116+
|
117+
LL | println!("{}", x == y.cloned());
118+
| +++++++++
119+
120+
error: aborting due to 7 previous errors
95121

96122
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)