Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

‎compiler/rustc_hir_typeck/messages.ftl‎

Lines changed: 4 additions & 0 deletions
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...

‎compiler/rustc_hir_typeck/src/errors.rs‎

Lines changed: 39 additions & 0 deletions
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]

‎compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs‎

Lines changed: 51 additions & 69 deletions
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

‎tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr‎

Lines changed: 1 addition & 1 deletion
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
| +++++++++

‎tests/ui/suggestions/copied-and-cloned.fixed‎

Lines changed: 15 additions & 1 deletion
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
}

‎tests/ui/suggestions/copied-and-cloned.rs‎

Lines changed: 15 additions & 1 deletion
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
}

‎tests/ui/suggestions/copied-and-cloned.stderr‎

Lines changed: 28 additions & 2 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /