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`
This commit is contained in:
commit
f9f674f2bc
@ -77,6 +77,10 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
|
|||||||
|
|
||||||
hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
|
hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
|
||||||
|
|
||||||
|
hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}`
|
||||||
|
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
|
||||||
|
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
|
||||||
|
|
||||||
hir_typeck_return_stmt_outside_of_fn_body =
|
hir_typeck_return_stmt_outside_of_fn_body =
|
||||||
{$statement_kind} statement outside of function body
|
{$statement_kind} statement outside of function body
|
||||||
.encl_body_label = the {$statement_kind} is part of this body...
|
.encl_body_label = the {$statement_kind} is part of this body...
|
||||||
|
@ -252,6 +252,45 @@ pub fn new() -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
pub enum OptionResultRefMismatch<'tcx> {
|
||||||
|
#[suggestion(
|
||||||
|
hir_typeck_option_result_copied,
|
||||||
|
code = ".copied()",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
Copied {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
def_path: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
hir_typeck_option_result_cloned,
|
||||||
|
code = ".cloned()",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
Cloned {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
def_path: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
hir_typeck_option_result_asref,
|
||||||
|
code = ".as_ref()",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
AsRef {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
def_path: String,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
expr_ty: Ty<'tcx>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_typeck_const_select_must_be_const)]
|
#[diag(hir_typeck_const_select_must_be_const)]
|
||||||
#[help]
|
#[help]
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use super::FnCtxt;
|
use super::FnCtxt;
|
||||||
|
|
||||||
use crate::errors::{
|
use crate::errors;
|
||||||
AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
|
|
||||||
};
|
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||||
@ -434,7 +432,7 @@ pub fn suggest_deref_ref_or_into(
|
|||||||
// FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
|
// FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
|
||||||
// but those checks need to be a bit more delicate and the benefit is diminishing.
|
// but those checks need to be a bit more delicate and the benefit is diminishing.
|
||||||
if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
|
if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
|
||||||
err.subdiagnostic(SuggestConvertViaMethod {
|
err.subdiagnostic(errors::SuggestConvertViaMethod {
|
||||||
span: expr.span.shrink_to_hi(),
|
span: expr.span.shrink_to_hi(),
|
||||||
sugg: ".as_ref()",
|
sugg: ".as_ref()",
|
||||||
expected,
|
expected,
|
||||||
@ -447,7 +445,7 @@ pub fn suggest_deref_ref_or_into(
|
|||||||
&& self.can_eq(self.param_env, deref_ty, peeled)
|
&& self.can_eq(self.param_env, deref_ty, peeled)
|
||||||
&& error_tys_equate_as_ref
|
&& error_tys_equate_as_ref
|
||||||
{
|
{
|
||||||
err.subdiagnostic(SuggestConvertViaMethod {
|
err.subdiagnostic(errors::SuggestConvertViaMethod {
|
||||||
span: expr.span.shrink_to_hi(),
|
span: expr.span.shrink_to_hi(),
|
||||||
sugg: ".as_deref()",
|
sugg: ".as_deref()",
|
||||||
expected,
|
expected,
|
||||||
@ -521,7 +519,7 @@ pub(in super::super) fn suggest_boxing_when_appropriate(
|
|||||||
if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
|
if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
|
||||||
let suggest_boxing = match found.kind() {
|
let suggest_boxing = match found.kind() {
|
||||||
ty::Tuple(tuple) if tuple.is_empty() => {
|
ty::Tuple(tuple) if tuple.is_empty() => {
|
||||||
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
|
errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
|
||||||
}
|
}
|
||||||
ty::Generator(def_id, ..)
|
ty::Generator(def_id, ..)
|
||||||
if matches!(
|
if matches!(
|
||||||
@ -529,9 +527,12 @@ pub(in super::super) fn suggest_boxing_when_appropriate(
|
|||||||
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
|
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
SuggestBoxing::AsyncBody
|
errors::SuggestBoxing::AsyncBody
|
||||||
}
|
}
|
||||||
_ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() },
|
_ => errors::SuggestBoxing::Other {
|
||||||
|
start: span.shrink_to_lo(),
|
||||||
|
end: span.shrink_to_hi(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
err.subdiagnostic(suggest_boxing);
|
err.subdiagnostic(suggest_boxing);
|
||||||
|
|
||||||
@ -756,23 +757,23 @@ pub(in super::super) fn suggest_missing_return_type(
|
|||||||
match &fn_decl.output {
|
match &fn_decl.output {
|
||||||
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
|
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
|
||||||
// `fn main()` must return `()`, do not suggest changing return type
|
// `fn main()` must return `()`, do not suggest changing return type
|
||||||
err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
|
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
|
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
|
||||||
if let Some(found) = found.make_suggestable(self.tcx, false) {
|
if let Some(found) = found.make_suggestable(self.tcx, false) {
|
||||||
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
||||||
return true;
|
return true;
|
||||||
} else if let ty::Closure(_, args) = found.kind()
|
} else if let ty::Closure(_, args) = found.kind()
|
||||||
// FIXME(compiler-errors): Get better at printing binders...
|
// FIXME(compiler-errors): Get better at printing binders...
|
||||||
&& let closure = args.as_closure()
|
&& let closure = args.as_closure()
|
||||||
&& closure.sig().is_suggestable(self.tcx, false)
|
&& closure.sig().is_suggestable(self.tcx, false)
|
||||||
{
|
{
|
||||||
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
|
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// FIXME: if `found` could be `impl Iterator` we should suggest that.
|
// FIXME: if `found` could be `impl Iterator` we should suggest that.
|
||||||
err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
|
err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,10 +795,10 @@ pub(in super::super) fn suggest_missing_return_type(
|
|||||||
debug!(?found);
|
debug!(?found);
|
||||||
if found.is_suggestable(self.tcx, false) {
|
if found.is_suggestable(self.tcx, false) {
|
||||||
if term.span.is_empty() {
|
if term.span.is_empty() {
|
||||||
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
|
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,7 +814,7 @@ pub(in super::super) fn suggest_missing_return_type(
|
|||||||
let ty = self.normalize(span, ty);
|
let ty = self.normalize(span, ty);
|
||||||
let ty = self.tcx.erase_late_bound_regions(ty);
|
let ty = self.tcx.erase_late_bound_regions(ty);
|
||||||
if self.can_coerce(expected, ty) {
|
if self.can_coerce(expected, ty) {
|
||||||
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
|
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
|
||||||
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
|
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1103,65 +1104,46 @@ pub(crate) fn suggest_copied_cloned_or_as_ref(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut suggest_copied_cloned_or_as_ref = || {
|
if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
|
||||||
|
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
|
||||||
|
|| Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
|
||||||
|
{
|
||||||
let expr_inner_ty = args.type_at(0);
|
let expr_inner_ty = args.type_at(0);
|
||||||
let expected_inner_ty = expected_args.type_at(0);
|
let expected_inner_ty = expected_args.type_at(0);
|
||||||
if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
|
if let &ty::Ref(_, ty, mutability) = expr_inner_ty.kind()
|
||||||
&& self.can_eq(self.param_env, ty, expected_inner_ty)
|
&& self.can_eq(self.param_env, ty, expected_inner_ty)
|
||||||
{
|
|
||||||
let def_path = self.tcx.def_path_str(adt_def.did());
|
|
||||||
if self.type_is_copy_modulo_regions(self.param_env, ty) {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
expr.span.shrink_to_hi(),
|
|
||||||
format!(
|
|
||||||
"use `{def_path}::copied` to copy the value inside the `{def_path}`"
|
|
||||||
),
|
|
||||||
".copied()",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
} else if let Some(expected_ty_expr) = expected_ty_expr {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
expected_ty_expr.span.shrink_to_hi(),
|
|
||||||
format!(
|
|
||||||
"use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
|
|
||||||
),
|
|
||||||
".as_ref()",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
|
|
||||||
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
|
|
||||||
self,
|
|
||||||
self.param_env,
|
|
||||||
ty,
|
|
||||||
clone_did,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
diag.span_suggestion_verbose(
|
let def_path = self.tcx.def_path_str(adt_def.did());
|
||||||
expr.span.shrink_to_hi(),
|
let span = expr.span.shrink_to_hi();
|
||||||
format!(
|
let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
|
||||||
"use `{def_path}::cloned` to clone the value inside the `{def_path}`"
|
errors::OptionResultRefMismatch::Copied {
|
||||||
),
|
span, def_path
|
||||||
".cloned()",
|
}
|
||||||
Applicability::MachineApplicable,
|
} else if let Some(expected_ty_expr) = expected_ty_expr
|
||||||
);
|
// FIXME: suggest changes to both expressions to convert both to
|
||||||
|
// Option/Result<&T>
|
||||||
|
&& mutability.is_not()
|
||||||
|
{
|
||||||
|
errors::OptionResultRefMismatch::AsRef {
|
||||||
|
span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path
|
||||||
|
}
|
||||||
|
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
|
||||||
|
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
|
||||||
|
self,
|
||||||
|
self.param_env,
|
||||||
|
ty,
|
||||||
|
clone_did,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
errors::OptionResultRefMismatch::Cloned {
|
||||||
|
span, def_path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
diag.subdiagnostic(subdiag);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
|
|
||||||
&& adt_def.did() == result_did
|
|
||||||
// Check that the error types are equal
|
|
||||||
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
|
|
||||||
{
|
|
||||||
return suggest_copied_cloned_or_as_ref();
|
|
||||||
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
|
|
||||||
&& adt_def.did() == option_did
|
|
||||||
{
|
|
||||||
return suggest_copied_cloned_or_as_ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -6,7 +6,7 @@ LL | debug_assert_eq!(iter.next(), Some(value));
|
|||||||
|
|
|
|
||||||
= note: expected enum `Option<<I as Iterator>::Item>`
|
= note: expected enum `Option<<I as Iterator>::Item>`
|
||||||
found enum `Option<&<I as Iterator>::Item>`
|
found enum `Option<&<I as Iterator>::Item>`
|
||||||
help: use `Option::as_ref()` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
|
help: use `Option::as_ref` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
|
||||||
|
|
|
|
||||||
LL | debug_assert_eq!(iter.next().as_ref(), Some(value));
|
LL | debug_assert_eq!(iter.next().as_ref(), Some(value));
|
||||||
| +++++++++
|
| +++++++++
|
||||||
|
@ -26,6 +26,20 @@ fn main() {
|
|||||||
let y = Some(&s);
|
let y = Some(&s);
|
||||||
println!("{}", x.as_ref() == y);
|
println!("{}", x.as_ref() == y);
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
|
//~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
|
||||||
|
|
||||||
|
|
||||||
|
let mut s = ();
|
||||||
|
let x = Some(s);
|
||||||
|
let y = Some(&mut s);
|
||||||
|
println!("{}", x == y.copied());
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP use `Option::copied` to copy the value inside the `Option`
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
let x = Some(s.clone());
|
||||||
|
let y = Some(&mut s);
|
||||||
|
println!("{}", x == y.cloned());
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP use `Option::cloned` to clone the value inside the `Option`
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,20 @@ fn main() {
|
|||||||
let y = Some(&s);
|
let y = Some(&s);
|
||||||
println!("{}", x == y);
|
println!("{}", x == y);
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
|
//~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
|
||||||
|
|
||||||
|
|
||||||
|
let mut s = ();
|
||||||
|
let x = Some(s);
|
||||||
|
let y = Some(&mut s);
|
||||||
|
println!("{}", x == y);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP use `Option::copied` to copy the value inside the `Option`
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
let x = Some(s.clone());
|
||||||
|
let y = Some(&mut s);
|
||||||
|
println!("{}", x == y);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP use `Option::cloned` to clone the value inside the `Option`
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,37 @@ LL | println!("{}", x == y);
|
|||||||
|
|
|
|
||||||
= note: expected enum `Option<String>`
|
= note: expected enum `Option<String>`
|
||||||
found enum `Option<&String>`
|
found enum `Option<&String>`
|
||||||
help: use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
|
help: use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
|
||||||
|
|
|
|
||||||
LL | println!("{}", x.as_ref() == y);
|
LL | println!("{}", x.as_ref() == y);
|
||||||
| +++++++++
|
| +++++++++
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/copied-and-cloned.rs:35:25
|
||||||
|
|
|
||||||
|
LL | println!("{}", x == y);
|
||||||
|
| ^ expected `Option<()>`, found `Option<&mut ()>`
|
||||||
|
|
|
||||||
|
= note: expected enum `Option<()>`
|
||||||
|
found enum `Option<&mut ()>`
|
||||||
|
help: use `Option::copied` to copy the value inside the `Option`
|
||||||
|
|
|
||||||
|
LL | println!("{}", x == y.copied());
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/copied-and-cloned.rs:42:25
|
||||||
|
|
|
||||||
|
LL | println!("{}", x == y);
|
||||||
|
| ^ expected `Option<String>`, found `Option<&mut String>`
|
||||||
|
|
|
||||||
|
= note: expected enum `Option<String>`
|
||||||
|
found enum `Option<&mut String>`
|
||||||
|
help: use `Option::cloned` to clone the value inside the `Option`
|
||||||
|
|
|
||||||
|
LL | println!("{}", x == y.cloned());
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
Loading…
Reference in New Issue
Block a user