Migrate SuggestAsRefWhereAppropriate

This commit is contained in:
IQuant 2023-03-03 15:41:22 +03:00
parent 37f55691f4
commit b36abea285
3 changed files with 131 additions and 29 deletions

View file

@ -353,3 +353,10 @@ infer_fps_use_ref = consider using a reference
infer_fps_remove_ref = consider removing the reference
infer_fps_cast = consider casting to a fn pointer
infer_fps_items_are_distinct = fn items are distinct from fn pointers
infer_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
infer_fn_uniq_types = different fn items have unique types, even if their signatures are the same
infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`

View file

@ -1158,6 +1158,14 @@ pub struct OpaqueCapturesLifetime<'tcx> {
pub opaque_ty: Ty<'tcx>,
}
pub struct DiagArg<T>(pub T);
impl<T: ToString> IntoDiagnosticArg for DiagArg<T> {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
self.0.to_string().into_diagnostic_arg()
}
}
#[derive(Subdiagnostic)]
pub enum FunctionPointerSuggestion<'a> {
#[suggestion(
@ -1212,8 +1220,72 @@ pub enum FunctionPointerSuggestion<'a> {
#[skip_arg]
sig: Binder<'a, FnSig<'a>>,
},
#[suggestion(
infer_fps_cast_both,
code = "{fn_name} as {found_sig}",
style = "hidden",
applicability = "maybe-incorrect"
)]
CastBoth {
#[primary_span]
span: Span,
#[skip_arg]
fn_name: String,
#[skip_arg]
found_sig: Binder<'a, FnSig<'a>>,
expected_sig: DiagArg<Binder<'a, FnSig<'a>>>,
},
#[suggestion(
infer_fps_cast_both,
code = "&({fn_name} as {found_sig})",
style = "hidden",
applicability = "maybe-incorrect"
)]
CastBothRef {
#[primary_span]
span: Span,
#[skip_arg]
fn_name: String,
#[skip_arg]
found_sig: Binder<'a, FnSig<'a>>,
expected_sig: DiagArg<Binder<'a, FnSig<'a>>>,
},
}
#[derive(Subdiagnostic)]
#[note(infer_fps_items_are_distinct)]
pub struct FnItemsAreDistinct;
#[derive(Subdiagnostic)]
#[note(infer_fn_uniq_types)]
pub struct FnUniqTypes;
#[derive(Subdiagnostic)]
#[help(infer_fn_uniq_types)]
pub struct FnConsiderCasting {
pub casting: String,
}
#[derive(Subdiagnostic)]
pub enum SuggestAsRefWhereAppropriate<'a> {
#[suggestion(
infer_sarwa_option,
code = "{snippet}.as_ref()",
applicability = "machine-applicable"
)]
Option {
#[primary_span]
span: Span,
snippet: &'a str,
},
#[suggestion(
infer_sarwa_result,
code = "{snippet}.as_ref()",
applicability = "machine-applicable"
)]
Result {
#[primary_span]
span: Span,
snippet: &'a str,
},
}

View file

@ -13,12 +13,19 @@ use rustc_span::{sym, BytePos, Span};
use rustc_target::abi::FieldIdx;
use crate::errors::{
ConsiderAddingAwait, FnItemsAreDistinct, FunctionPointerSuggestion, SuggAddLetForLetChains,
ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAsRefWhereAppropriate,
SuggestRemoveSemiOrReturnBinding,
};
use super::TypeErrCtxt;
#[derive(Clone, Copy)]
pub enum SuggestAsRefKind {
Option,
Result,
}
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub(super) fn suggest_remove_semi_or_return_binding(
&self,
@ -322,15 +329,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
diag: &mut Diagnostic,
) {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
&& let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
{
diag.span_suggestion(
span,
msg,
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
format!("{}.as_ref()", snippet.trim_start_matches('&')),
Applicability::MachineApplicable,
);
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
let snippet = snippet.trim_start_matches('&');
let subdiag = match msg {
SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
};
diag.subdiagnostic(subdiag);
}
}
@ -384,7 +391,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
diag.note("different fn items have unique types, even if their signatures are the same");
diag.subdiagnostic(FnUniqTypes);
}
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
@ -398,16 +405,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
let sug = if found.is_ref() {
format!("&({fn_name} as {found_sig})")
FunctionPointerSuggestion::CastBothRef {
span,
fn_name,
found_sig: *found_sig,
expected_sig: DiagArg(*expected_sig),
}
} else {
format!("{fn_name} as {found_sig}")
FunctionPointerSuggestion::CastBoth {
span,
fn_name,
found_sig: *found_sig,
expected_sig: DiagArg(*expected_sig),
}
};
let msg = format!(
"consider casting both fn items to fn pointers using `as {expected_sig}`"
);
diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
diag.subdiagnostic(sug);
}
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
let expected_sig =
@ -426,7 +439,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!("{fn_name} as {found_sig}")
};
diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
diag.subdiagnostic(FnConsiderCasting { casting });
}
_ => {
return;
@ -434,23 +447,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
}
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
pub fn should_suggest_as_ref_kind(
&self,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> Option<SuggestAsRefKind> {
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
(expected.kind(), found.kind())
{
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
if exp_def == &found_def {
let have_as_ref = &[
(
sym::Option,
"you can convert from `&Option<T>` to `Option<&T>` using \
`.as_ref()`",
),
(
sym::Result,
"you can convert from `&Result<T, E>` to \
`Result<&T, &E>` using `.as_ref()`",
),
(sym::Option, SuggestAsRefKind::Option),
(sym::Result, SuggestAsRefKind::Result),
];
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
@ -484,6 +493,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
None
}
// FIXME: Remove once rustc_hir_typeck is migrated to Diagnostics
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
match self.should_suggest_as_ref_kind(expected, found) {
Some(SuggestAsRefKind::Option) => Some(
"you can convert from `&Option<T>` to `Option<&T>` using \
`.as_ref()`",
),
Some(SuggestAsRefKind::Result) => Some(
"you can convert from `&Result<T, E>` to \
`Result<&T, &E>` using `.as_ref()`",
),
None => None,
}
}
/// Try to find code with pattern `if Some(..) = expr`
/// use a `visitor` to mark the `if` which its span contains given error span,
/// and then try to find a assignment in the `cond` part, which span is equal with error span