@@ -13,12 +13,19 @@ use rustc_span::{sym, BytePos, Span};
1313use rustc_target:: abi:: FieldIdx ;
1414
1515use crate :: errors:: {
16- ConsiderAddingAwait , FnItemsAreDistinct , FunctionPointerSuggestion , SuggAddLetForLetChains ,
16+ ConsiderAddingAwait , DiagArg , FnConsiderCasting , FnItemsAreDistinct , FnUniqTypes ,
17+ FunctionPointerSuggestion , SuggAddLetForLetChains , SuggestAsRefWhereAppropriate ,
1718 SuggestRemoveSemiOrReturnBinding ,
1819} ;
1920
2021use super :: TypeErrCtxt ;
2122
23+ #[ derive( Clone , Copy ) ]
24+ pub enum SuggestAsRefKind {
25+ Option ,
26+ Result ,
27+ }
28+
2229impl < ' 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