99use std:: mem;
1010use syntax:: print:: pprust;
1111use rustc:: lint;
12+ use rustc:: lint:: builtin:: { BuiltinLintDiagnostics , NESTED_IMPL_TRAIT } ;
1213use rustc:: session:: Session ;
1314use rustc_data_structures:: fx:: FxHashMap ;
1415use syntax:: ast:: * ;
@@ -23,6 +24,31 @@ use syntax_pos::Span;
2324use errors:: Applicability ;
2425use log:: debug;
2526
27+ #[ derive( Copy , Clone , Debug ) ]
28+ struct OuterImplTrait {
29+ span : Span ,
30+
31+ /// rust-lang/rust#57979: a bug in original implementation caused
32+ /// us to fail sometimes to record an outer `impl Trait`.
33+ /// Therefore, in order to reliably issue a warning (rather than
34+ /// an error) in the *precise* places where we are newly injecting
35+ /// the diagnostic, we have to distinguish between the places
36+ /// where the outer `impl Trait` has always been recorded, versus
37+ /// the places where it has only recently started being recorded.
38+ only_recorded_since_pull_request_57730 : bool ,
39+ }
40+
41+ impl OuterImplTrait {
42+ /// This controls whether we should downgrade the nested impl
43+ /// trait diagnostic to a warning rather than an error, based on
44+ /// whether the outer impl trait had been improperly skipped in
45+ /// earlier implementations of the analysis on the stable
46+ /// compiler.
47+ fn should_warn_instead_of_error ( & self ) -> bool {
48+ self . only_recorded_since_pull_request_57730
49+ }
50+ }
51+
2652struct AstValidator < ' a > {
2753 session : & ' a Session ,
2854 has_proc_macro_decls : bool ,
@@ -31,31 +57,83 @@ struct AstValidator<'a> {
3157 // Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
3258 // Nested `impl Trait` _is_ allowed in associated type position,
3359 // e.g `impl Iterator<Item=impl Debug>`
34- outer_impl_trait : Option < Span > ,
60+ outer_impl_trait : Option < OuterImplTrait > ,
3561
3662 // Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
3763 // or `Foo::Bar<impl Trait>`
3864 is_impl_trait_banned : bool ,
65+
66+ // rust-lang/rust#57979: the ban of nested `impl Trait` was buggy
67+ // until PRs #57730 and #57981 landed: it would jump directly to
68+ // walk_ty rather than visit_ty (or skip recurring entirely for
69+ // impl trait in projections), and thus miss some cases. We track
70+ // whether we should downgrade to a warning for short-term via
71+ // these booleans.
72+ warning_period_57979_didnt_record_next_impl_trait : bool ,
73+ warning_period_57979_impl_trait_in_proj : bool ,
3974}
4075
4176impl < ' a > AstValidator < ' a > {
77+ fn with_impl_trait_in_proj_warning < T > ( & mut self , v : bool , f : impl FnOnce ( & mut Self ) -> T ) -> T {
78+ let old = mem:: replace ( & mut self . warning_period_57979_impl_trait_in_proj , v) ;
79+ let ret = f ( self ) ;
80+ self . warning_period_57979_impl_trait_in_proj = old;
81+ ret
82+ }
83+
4284 fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
4385 let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
4486 f ( self ) ;
4587 self . is_impl_trait_banned = old;
4688 }
4789
48- fn with_impl_trait ( & mut self , outer_impl_trait : Option < Span > , f : impl FnOnce ( & mut Self ) ) {
49- let old = mem:: replace ( & mut self . outer_impl_trait , outer_impl_trait ) ;
90+ fn with_impl_trait ( & mut self , outer : Option < OuterImplTrait > , f : impl FnOnce ( & mut Self ) ) {
91+ let old = mem:: replace ( & mut self . outer_impl_trait , outer ) ;
5092 f ( self ) ;
5193 self . outer_impl_trait = old;
5294 }
5395
96+ fn visit_assoc_type_binding_from_generic_args ( & mut self , type_binding : & ' a TypeBinding ) {
97+ // rust-lang/rust#57979: bug in old visit_generic_args called
98+ // walk_ty rather than visit_ty, skipping outer `impl Trait`
99+ // if it happened to occur at `type_binding.ty`
100+ if let TyKind :: ImplTrait ( ..) = type_binding. ty . node {
101+ self . warning_period_57979_didnt_record_next_impl_trait = true ;
102+ }
103+ self . visit_assoc_type_binding ( type_binding) ;
104+ }
105+
106+ fn visit_ty_from_generic_args ( & mut self , ty : & ' a Ty ) {
107+ // rust-lang/rust#57979: bug in old visit_generic_args called
108+ // walk_ty rather than visit_ty, skippping outer `impl Trait`
109+ // if it happened to occur at `ty`
110+ if let TyKind :: ImplTrait ( ..) = ty. node {
111+ self . warning_period_57979_didnt_record_next_impl_trait = true ;
112+ }
113+ self . visit_ty ( ty) ;
114+ }
115+
116+ fn outer_impl_trait ( & mut self , span : Span ) -> OuterImplTrait {
117+ let only_recorded_since_pull_request_57730 =
118+ self . warning_period_57979_didnt_record_next_impl_trait ;
119+
120+ // (this flag is designed to be set to true and then only
121+ // reach the construction point for the outer impl trait once,
122+ // so its safe and easiest to unconditionally reset it to
123+ // false)
124+ self . warning_period_57979_didnt_record_next_impl_trait = false ;
125+
126+ OuterImplTrait {
127+ span, only_recorded_since_pull_request_57730,
128+ }
129+ }
130+
54131 // Mirrors visit::walk_ty, but tracks relevant state
55132 fn walk_ty ( & mut self , t : & ' a Ty ) {
56133 match t. node {
57134 TyKind :: ImplTrait ( ..) => {
58- self . with_impl_trait ( Some ( t. span ) , |this| visit:: walk_ty ( this, t) )
135+ let outer_impl_trait = self . outer_impl_trait ( t. span ) ;
136+ self . with_impl_trait ( Some ( outer_impl_trait) , |this| visit:: walk_ty ( this, t) )
59137 }
60138 TyKind :: Path ( ref qself, ref path) => {
61139 // We allow these:
@@ -406,22 +484,41 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
406484 }
407485 TyKind :: ImplTrait ( _, ref bounds) => {
408486 if self . is_impl_trait_banned {
409- struct_span_err ! ( self . session, ty. span, E0667 ,
410- "`impl Trait` is not allowed in path parameters" ) . emit ( ) ;
487+ if self . warning_period_57979_impl_trait_in_proj {
488+ self . session . buffer_lint (
489+ NESTED_IMPL_TRAIT , ty. id , ty. span ,
490+ "`impl Trait` is not allowed in path parameters" ) ;
491+ } else {
492+ struct_span_err ! ( self . session, ty. span, E0667 ,
493+ "`impl Trait` is not allowed in path parameters" ) . emit ( ) ;
494+ }
411495 }
412496
413497 if let Some ( outer_impl_trait) = self . outer_impl_trait {
414- struct_span_err ! ( self . session, ty. span, E0666 ,
415- "nested `impl Trait` is not allowed" )
416- . span_label ( outer_impl_trait, "outer `impl Trait`" )
417- . span_label ( ty. span , "nested `impl Trait` here" )
418- . emit ( ) ;
419-
498+ if outer_impl_trait. should_warn_instead_of_error ( ) {
499+ self . session . buffer_lint_with_diagnostic (
500+ NESTED_IMPL_TRAIT , ty. id , ty. span ,
501+ "nested `impl Trait` is not allowed" ,
502+ BuiltinLintDiagnostics :: NestedImplTrait {
503+ outer_impl_trait_span : outer_impl_trait. span ,
504+ inner_impl_trait_span : ty. span ,
505+ } ) ;
506+ } else {
507+ struct_span_err ! ( self . session, ty. span, E0666 ,
508+ "nested `impl Trait` is not allowed" )
509+ . span_label ( outer_impl_trait. span , "outer `impl Trait`" )
510+ . span_label ( ty. span , "nested `impl Trait` here" )
511+ . emit ( ) ;
512+ }
420513 }
514+
421515 if !bounds. iter ( )
422516 . any ( |b| if let GenericBound :: Trait ( ..) = * b { true } else { false } ) {
423517 self . err_handler ( ) . span_err ( ty. span , "at least one trait must be specified" ) ;
424518 }
519+
520+ self . with_impl_trait_in_proj_warning ( true , |this| this. walk_ty ( ty) ) ;
521+ return ;
425522 }
426523 _ => { }
427524 }
@@ -606,18 +703,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
606703 GenericArg :: Const ( ..) => ParamKindOrd :: Const ,
607704 } , arg. span ( ) , None )
608705 } ) , GenericPosition :: Arg , generic_args. span ( ) ) ;
706+
609707 // Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
610708 // are allowed to contain nested `impl Trait`.
611709 self . with_impl_trait ( None , |this| {
612- walk_list ! ( this, visit_assoc_type_binding , & data. bindings) ;
710+ walk_list ! ( this, visit_assoc_type_binding_from_generic_args , & data. bindings) ;
613711 } ) ;
614712 }
615713 GenericArgs :: Parenthesized ( ref data) => {
616714 walk_list ! ( self , visit_ty, & data. inputs) ;
617715 if let Some ( ref type_) = data. output {
618716 // `-> Foo` syntax is essentially an associated type binding,
619717 // so it is also allowed to contain nested `impl Trait`.
620- self . with_impl_trait ( None , |this| this. visit_ty ( type_) ) ;
718+ self . with_impl_trait ( None , |this| this. visit_ty_from_generic_args ( type_) ) ;
621719 }
622720 }
623721 }
@@ -719,6 +817,8 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
719817 has_global_allocator : false ,
720818 outer_impl_trait : None ,
721819 is_impl_trait_banned : false ,
820+ warning_period_57979_didnt_record_next_impl_trait : false ,
821+ warning_period_57979_impl_trait_in_proj : false ,
722822 } ;
723823 visit:: walk_crate ( & mut validator, krate) ;
724824
0 commit comments