1
1
use crate :: diagnostics:: { ImportSuggestion , LabelSuggestion , TypoSuggestion } ;
2
2
use crate :: late:: lifetimes:: { ElisionFailureInfo , LifetimeContext } ;
3
- use crate :: late:: { LateResolutionVisitor , RibKind } ;
3
+ use crate :: late:: { AliasPossibility , LateResolutionVisitor , RibKind } ;
4
4
use crate :: path_names_to_string;
5
5
use crate :: { CrateLint , Module , ModuleKind , ModuleOrUniformRoot } ;
6
6
use crate :: { PathResult , PathSource , Segment } ;
7
7
8
8
use rustc_ast:: util:: lev_distance:: find_best_match_for_name;
9
9
use rustc_ast:: visit:: FnKind ;
10
10
use rustc_ast:: { self as ast, Expr , ExprKind , Item , ItemKind , NodeId , Path , Ty , TyKind } ;
11
+ use rustc_ast_pretty:: pprust:: path_segment_to_string;
11
12
use rustc_data_structures:: fx:: FxHashSet ;
12
13
use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
13
14
use rustc_hir as hir;
@@ -19,7 +20,7 @@ use rustc_session::config::nightly_options;
19
20
use rustc_session:: parse:: feature_err;
20
21
use rustc_span:: hygiene:: MacroKind ;
21
22
use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
22
- use rustc_span:: { BytePos , Span , DUMMY_SP } ;
23
+ use rustc_span:: { BytePos , MultiSpan , Span , DUMMY_SP } ;
23
24
24
25
use tracing:: debug;
25
26
@@ -439,27 +440,213 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
439
440
}
440
441
}
441
442
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 ( ) ,
455
479
) ;
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
+ }
456
503
}
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) ;
458
527
}
459
528
}
460
529
( err, candidates)
461
530
}
462
531
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
+
463
650
/// Check if the source is call expression and the first argument is `self`. If true,
464
651
/// return the span of whole call and the span for all arguments expect the first one (`self`).
465
652
fn call_has_self_arg ( & self , source : PathSource < ' _ > ) -> Option < ( Span , Option < Span > ) > {
0 commit comments