Skip to content

Commit b36abea

Browse files
committed
Migrate SuggestAsRefWhereAppropriate
1 parent 37f5569 commit b36abea

File tree

3 files changed

+131
-29
lines changed

3 files changed

+131
-29
lines changed

compiler/rustc_infer/messages.ftl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,10 @@ infer_fps_use_ref = consider using a reference
353353
infer_fps_remove_ref = consider removing the reference
354354
infer_fps_cast = consider casting to a fn pointer
355355
infer_fps_items_are_distinct = fn items are distinct from fn pointers
356+
infer_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
357+
358+
infer_fn_uniq_types = different fn items have unique types, even if their signatures are the same
359+
infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
360+
361+
infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
362+
infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`

compiler/rustc_infer/src/errors/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,14 @@ pub struct OpaqueCapturesLifetime<'tcx> {
11581158
pub opaque_ty: Ty<'tcx>,
11591159
}
11601160

1161+
pub struct DiagArg<T>(pub T);
1162+
1163+
impl<T: ToString> IntoDiagnosticArg for DiagArg<T> {
1164+
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
1165+
self.0.to_string().into_diagnostic_arg()
1166+
}
1167+
}
1168+
11611169
#[derive(Subdiagnostic)]
11621170
pub enum FunctionPointerSuggestion<'a> {
11631171
#[suggestion(
@@ -1212,8 +1220,72 @@ pub enum FunctionPointerSuggestion<'a> {
12121220
#[skip_arg]
12131221
sig: Binder<'a, FnSig<'a>>,
12141222
},
1223+
#[suggestion(
1224+
infer_fps_cast_both,
1225+
code = "{fn_name} as {found_sig}",
1226+
style = "hidden",
1227+
applicability = "maybe-incorrect"
1228+
)]
1229+
CastBoth {
1230+
#[primary_span]
1231+
span: Span,
1232+
#[skip_arg]
1233+
fn_name: String,
1234+
#[skip_arg]
1235+
found_sig: Binder<'a, FnSig<'a>>,
1236+
expected_sig: DiagArg<Binder<'a, FnSig<'a>>>,
1237+
},
1238+
#[suggestion(
1239+
infer_fps_cast_both,
1240+
code = "&({fn_name} as {found_sig})",
1241+
style = "hidden",
1242+
applicability = "maybe-incorrect"
1243+
)]
1244+
CastBothRef {
1245+
#[primary_span]
1246+
span: Span,
1247+
#[skip_arg]
1248+
fn_name: String,
1249+
#[skip_arg]
1250+
found_sig: Binder<'a, FnSig<'a>>,
1251+
expected_sig: DiagArg<Binder<'a, FnSig<'a>>>,
1252+
},
12151253
}
12161254

12171255
#[derive(Subdiagnostic)]
12181256
#[note(infer_fps_items_are_distinct)]
12191257
pub struct FnItemsAreDistinct;
1258+
1259+
#[derive(Subdiagnostic)]
1260+
#[note(infer_fn_uniq_types)]
1261+
pub struct FnUniqTypes;
1262+
1263+
#[derive(Subdiagnostic)]
1264+
#[help(infer_fn_uniq_types)]
1265+
pub struct FnConsiderCasting {
1266+
pub casting: String,
1267+
}
1268+
1269+
#[derive(Subdiagnostic)]
1270+
pub enum SuggestAsRefWhereAppropriate<'a> {
1271+
#[suggestion(
1272+
infer_sarwa_option,
1273+
code = "{snippet}.as_ref()",
1274+
applicability = "machine-applicable"
1275+
)]
1276+
Option {
1277+
#[primary_span]
1278+
span: Span,
1279+
snippet: &'a str,
1280+
},
1281+
#[suggestion(
1282+
infer_sarwa_result,
1283+
code = "{snippet}.as_ref()",
1284+
applicability = "machine-applicable"
1285+
)]
1286+
Result {
1287+
#[primary_span]
1288+
span: Span,
1289+
snippet: &'a str,
1290+
},
1291+
}

compiler/rustc_infer/src/infer/error_reporting/suggest.rs

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ use rustc_span::{sym, BytePos, Span};
1313
use rustc_target::abi::FieldIdx;
1414

1515
use crate::errors::{
16-
ConsiderAddingAwait, FnItemsAreDistinct, FunctionPointerSuggestion, SuggAddLetForLetChains,
16+
ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
17+
FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAsRefWhereAppropriate,
1718
SuggestRemoveSemiOrReturnBinding,
1819
};
1920

2021
use super::TypeErrCtxt;
2122

23+
#[derive(Clone, Copy)]
24+
pub enum SuggestAsRefKind {
25+
Option,
26+
Result,
27+
}
28+
2229
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
2330
pub(super) fn suggest_remove_semi_or_return_binding(
2431
&self,
@@ -322,15 +329,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
322329
diag: &mut Diagnostic,
323330
) {
324331
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
325-
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
332+
&& let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
326333
{
327-
diag.span_suggestion(
328-
span,
329-
msg,
330-
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
331-
format!("{}.as_ref()", snippet.trim_start_matches('&')),
332-
Applicability::MachineApplicable,
333-
);
334+
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
335+
let snippet = snippet.trim_start_matches('&');
336+
let subdiag = match msg {
337+
SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
338+
SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
339+
};
340+
diag.subdiagnostic(subdiag);
334341
}
335342
}
336343

@@ -384,7 +391,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
384391
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
385392

386393
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
387-
diag.note("different fn items have unique types, even if their signatures are the same");
394+
diag.subdiagnostic(FnUniqTypes);
388395
}
389396

390397
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
@@ -398,16 +405,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
398405

399406
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
400407
let sug = if found.is_ref() {
401-
format!("&({fn_name} as {found_sig})")
408+
FunctionPointerSuggestion::CastBothRef {
409+
span,
410+
fn_name,
411+
found_sig: *found_sig,
412+
expected_sig: DiagArg(*expected_sig),
413+
}
402414
} else {
403-
format!("{fn_name} as {found_sig}")
415+
FunctionPointerSuggestion::CastBoth {
416+
span,
417+
fn_name,
418+
found_sig: *found_sig,
419+
expected_sig: DiagArg(*expected_sig),
420+
}
404421
};
405422

406-
let msg = format!(
407-
"consider casting both fn items to fn pointers using `as {expected_sig}`"
408-
);
409-
410-
diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
423+
diag.subdiagnostic(sug);
411424
}
412425
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
413426
let expected_sig =
@@ -426,31 +439,27 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
426439
format!("{fn_name} as {found_sig}")
427440
};
428441

429-
diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
442+
diag.subdiagnostic(FnConsiderCasting { casting });
430443
}
431444
_ => {
432445
return;
433446
}
434447
};
435448
}
436449

437-
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
450+
pub fn should_suggest_as_ref_kind(
451+
&self,
452+
expected: Ty<'tcx>,
453+
found: Ty<'tcx>,
454+
) -> Option<SuggestAsRefKind> {
438455
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
439456
(expected.kind(), found.kind())
440457
{
441458
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
442459
if exp_def == &found_def {
443460
let have_as_ref = &[
444-
(
445-
sym::Option,
446-
"you can convert from `&Option<T>` to `Option<&T>` using \
447-
`.as_ref()`",
448-
),
449-
(
450-
sym::Result,
451-
"you can convert from `&Result<T, E>` to \
452-
`Result<&T, &E>` using `.as_ref()`",
453-
),
461+
(sym::Option, SuggestAsRefKind::Option),
462+
(sym::Result, SuggestAsRefKind::Result),
454463
];
455464
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
456465
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
@@ -484,6 +493,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
484493
None
485494
}
486495

496+
// FIXME: Remove once rustc_hir_typeck is migrated to Diagnostics
497+
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
498+
match self.should_suggest_as_ref_kind(expected, found) {
499+
Some(SuggestAsRefKind::Option) => Some(
500+
"you can convert from `&Option<T>` to `Option<&T>` using \
501+
`.as_ref()`",
502+
),
503+
Some(SuggestAsRefKind::Result) => Some(
504+
"you can convert from `&Result<T, E>` to \
505+
`Result<&T, &E>` using `.as_ref()`",
506+
),
507+
None => None,
508+
}
509+
}
487510
/// Try to find code with pattern `if Some(..) = expr`
488511
/// use a `visitor` to mark the `if` which its span contains given error span,
489512
/// and then try to find a assignment in the `cond` part, which span is equal with error span

0 commit comments

Comments
 (0)