11use crate :: diagnostics:: { ImportSuggestion , LabelSuggestion , TypoSuggestion } ;
22use crate :: late:: lifetimes:: { ElisionFailureInfo , LifetimeContext } ;
3- use crate :: late:: { LateResolutionVisitor , RibKind } ;
3+ use crate :: late:: { AliasPossibility , LateResolutionVisitor , RibKind } ;
44use crate :: path_names_to_string;
55use crate :: { CrateLint , Module , ModuleKind , ModuleOrUniformRoot } ;
66use crate :: { PathResult , PathSource , Segment } ;
77
88use rustc_ast:: util:: lev_distance:: find_best_match_for_name;
99use rustc_ast:: visit:: FnKind ;
1010use rustc_ast:: { self as ast, Expr , ExprKind , Item , ItemKind , NodeId , Path , Ty , TyKind } ;
11+ use rustc_ast_pretty:: pprust:: path_segment_to_string;
1112use rustc_data_structures:: fx:: FxHashSet ;
1213use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
1314use rustc_hir as hir;
@@ -19,7 +20,7 @@ use rustc_session::config::nightly_options;
1920use rustc_session:: parse:: feature_err;
2021use rustc_span:: hygiene:: MacroKind ;
2122use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
22- use rustc_span:: { BytePos , Span , DUMMY_SP } ;
23+ use rustc_span:: { BytePos , MultiSpan , Span , DUMMY_SP } ;
2324
2425use tracing:: debug;
2526
@@ -439,27 +440,213 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
439440 }
440441 }
441442
442- if !self . type_ascription_suggestion ( & mut err, base_span)
443- && !self . r . add_typo_suggestion ( & mut err, typo_sugg, ident_span)
444- {
445- // Fallback label.
446- err. span_label ( base_span, fallback_label) ;
447-
448- match self . diagnostic_metadata . current_let_binding {
449- Some ( ( pat_sp, Some ( ty_sp) , None ) ) if ty_sp. contains ( base_span) && could_be_expr => {
450- err. span_suggestion_short (
451- pat_sp. between ( ty_sp) ,
452- "use `=` if you meant to assign" ,
453- " = " . to_string ( ) ,
454- Applicability :: MaybeIncorrect ,
443+ if !self . type_ascription_suggestion ( & mut err, base_span) {
444+ let mut fallback = false ;
445+ if let (
446+ PathSource :: Trait ( AliasPossibility :: Maybe ) ,
447+ Some ( Res :: Def ( DefKind :: Struct | DefKind :: Enum | DefKind :: Union , _) ) ,
448+ ) = ( source, res)
449+ {
450+ if let Some ( bounds @ [ _, .., _] ) = self . diagnostic_metadata . current_trait_object {
451+ fallback = true ;
452+ let spans: Vec < Span > = bounds
453+ . iter ( )
454+ . map ( |bound| bound. span ( ) )
455+ . filter ( |& sp| sp != base_span)
456+ . collect ( ) ;
457+
458+ let start_span = bounds. iter ( ) . map ( |bound| bound. span ( ) ) . next ( ) . unwrap ( ) ;
459+ // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
460+ let end_span = bounds. iter ( ) . map ( |bound| bound. span ( ) ) . last ( ) . unwrap ( ) ;
461+ // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
462+ let last_bound_span = spans. last ( ) . cloned ( ) . unwrap ( ) ;
463+ let mut multi_span: MultiSpan = spans. clone ( ) . into ( ) ;
464+ for sp in spans {
465+ let msg = if sp == last_bound_span {
466+ format ! (
467+ "...because of {} bound{}" ,
468+ if bounds. len( ) <= 2 { "this" } else { "these" } ,
469+ if bounds. len( ) <= 2 { "" } else { "s" } ,
470+ )
471+ } else {
472+ String :: new ( )
473+ } ;
474+ multi_span. push_span_label ( sp, msg) ;
475+ }
476+ multi_span. push_span_label (
477+ base_span,
478+ "expected this type to be a trait..." . to_string ( ) ,
455479 ) ;
480+ err. span_help (
481+ multi_span,
482+ "`+` is used to constrain a \" trait object\" type with lifetimes or \
483+ auto-traits; structs and enums can't be bound in that way",
484+ ) ;
485+ if bounds. iter ( ) . all ( |bound| match bound {
486+ ast:: GenericBound :: Outlives ( _) => true ,
487+ ast:: GenericBound :: Trait ( tr, _) => tr. span == base_span,
488+ } ) {
489+ let mut sugg = vec ! [ ] ;
490+ if base_span != start_span {
491+ sugg. push ( ( start_span. until ( base_span) , String :: new ( ) ) ) ;
492+ }
493+ if base_span != end_span {
494+ sugg. push ( ( base_span. shrink_to_hi ( ) . to ( end_span) , String :: new ( ) ) ) ;
495+ }
496+
497+ err. multipart_suggestion (
498+ "if you meant to use a type and not a trait here, remove the bounds" ,
499+ sugg,
500+ Applicability :: MaybeIncorrect ,
501+ ) ;
502+ }
456503 }
457- _ => { }
504+ }
505+
506+ fallback |= self . restrict_assoc_type_in_where_clause ( span, & mut err) ;
507+
508+ if !self . r . add_typo_suggestion ( & mut err, typo_sugg, ident_span) {
509+ fallback = true ;
510+ match self . diagnostic_metadata . current_let_binding {
511+ Some ( ( pat_sp, Some ( ty_sp) , None ) )
512+ if ty_sp. contains ( base_span) && could_be_expr =>
513+ {
514+ err. span_suggestion_short (
515+ pat_sp. between ( ty_sp) ,
516+ "use `=` if you meant to assign" ,
517+ " = " . to_string ( ) ,
518+ Applicability :: MaybeIncorrect ,
519+ ) ;
520+ }
521+ _ => { }
522+ }
523+ }
524+ if fallback {
525+ // Fallback label.
526+ err. span_label ( base_span, fallback_label) ;
458527 }
459528 }
460529 ( err, candidates)
461530 }
462531
532+ /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
533+ fn restrict_assoc_type_in_where_clause (
534+ & mut self ,
535+ span : Span ,
536+ err : & mut DiagnosticBuilder < ' _ > ,
537+ ) -> bool {
538+ // Detect that we are actually in a `where` predicate.
539+ let ( bounded_ty, bounds, where_span) =
540+ if let Some ( ast:: WherePredicate :: BoundPredicate ( ast:: WhereBoundPredicate {
541+ bounded_ty,
542+ bound_generic_params,
543+ bounds,
544+ span,
545+ } ) ) = self . diagnostic_metadata . current_where_predicate
546+ {
547+ if !bound_generic_params. is_empty ( ) {
548+ return false ;
549+ }
550+ ( bounded_ty, bounds, span)
551+ } else {
552+ return false ;
553+ } ;
554+
555+ // Confirm that the target is an associated type.
556+ let ( ty, position, path) = if let ast:: TyKind :: Path (
557+ Some ( ast:: QSelf { ty, position, .. } ) ,
558+ path,
559+ ) = & bounded_ty. kind
560+ {
561+ // use this to verify that ident is a type param.
562+ let partial_res = if let Ok ( Some ( partial_res) ) = self . resolve_qpath_anywhere (
563+ bounded_ty. id ,
564+ None ,
565+ & Segment :: from_path ( path) ,
566+ Namespace :: TypeNS ,
567+ span,
568+ true ,
569+ CrateLint :: No ,
570+ ) {
571+ partial_res
572+ } else {
573+ return false ;
574+ } ;
575+ if !( matches ! (
576+ partial_res. base_res( ) ,
577+ hir:: def:: Res :: Def ( hir:: def:: DefKind :: AssocTy , _)
578+ ) && partial_res. unresolved_segments ( ) == 0 )
579+ {
580+ return false ;
581+ }
582+ ( ty, position, path)
583+ } else {
584+ return false ;
585+ } ;
586+
587+ if let ast:: TyKind :: Path ( None , type_param_path) = & ty. peel_refs ( ) . kind {
588+ // Confirm that the `SelfTy` is a type parameter.
589+ let partial_res = if let Ok ( Some ( partial_res) ) = self . resolve_qpath_anywhere (
590+ bounded_ty. id ,
591+ None ,
592+ & Segment :: from_path ( type_param_path) ,
593+ Namespace :: TypeNS ,
594+ span,
595+ true ,
596+ CrateLint :: No ,
597+ ) {
598+ partial_res
599+ } else {
600+ return false ;
601+ } ;
602+ if !( matches ! (
603+ partial_res. base_res( ) ,
604+ hir:: def:: Res :: Def ( hir:: def:: DefKind :: TyParam , _)
605+ ) && partial_res. unresolved_segments ( ) == 0 )
606+ {
607+ return false ;
608+ }
609+ if let (
610+ [ ast:: PathSegment { ident : constrain_ident, args : None , .. } ] ,
611+ [ ast:: GenericBound :: Trait ( poly_trait_ref, ast:: TraitBoundModifier :: None ) ] ,
612+ ) = ( & type_param_path. segments [ ..] , & bounds[ ..] )
613+ {
614+ if let [ ast:: PathSegment { ident, args : None , .. } ] =
615+ & poly_trait_ref. trait_ref . path . segments [ ..]
616+ {
617+ if ident. span == span {
618+ err. span_suggestion_verbose (
619+ * where_span,
620+ & format ! ( "constrain the associated type to `{}`" , ident) ,
621+ format ! (
622+ "{}: {}<{} = {}>" ,
623+ self . r
624+ . session
625+ . source_map( )
626+ . span_to_snippet( ty. span) // Account for `<&'a T as Foo>::Bar`.
627+ . unwrap_or_else( |_| constrain_ident. to_string( ) ) ,
628+ path. segments[ ..* position]
629+ . iter( )
630+ . map( |segment| path_segment_to_string( segment) )
631+ . collect:: <Vec <_>>( )
632+ . join( "::" ) ,
633+ path. segments[ * position..]
634+ . iter( )
635+ . map( |segment| path_segment_to_string( segment) )
636+ . collect:: <Vec <_>>( )
637+ . join( "::" ) ,
638+ ident,
639+ ) ,
640+ Applicability :: MaybeIncorrect ,
641+ ) ;
642+ }
643+ return true ;
644+ }
645+ }
646+ }
647+ false
648+ }
649+
463650 /// Check if the source is call expression and the first argument is `self`. If true,
464651 /// return the span of whole call and the span for all arguments expect the first one (`self`).
465652 fn call_has_self_arg ( & self , source : PathSource < ' _ > ) -> Option < ( Span , Option < Span > ) > {
0 commit comments