9
9
use std:: mem;
10
10
use syntax:: print:: pprust;
11
11
use rustc:: lint;
12
+ use rustc:: lint:: builtin:: { BuiltinLintDiagnostics , NESTED_IMPL_TRAIT } ;
12
13
use rustc:: session:: Session ;
13
14
use rustc_data_structures:: fx:: FxHashMap ;
14
15
use syntax:: ast:: * ;
@@ -23,6 +24,31 @@ use syntax_pos::Span;
23
24
use errors:: Applicability ;
24
25
use log:: debug;
25
26
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
+
26
52
struct AstValidator < ' a > {
27
53
session : & ' a Session ,
28
54
has_proc_macro_decls : bool ,
@@ -31,31 +57,83 @@ struct AstValidator<'a> {
31
57
// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
32
58
// Nested `impl Trait` _is_ allowed in associated type position,
33
59
// e.g `impl Iterator<Item=impl Debug>`
34
- outer_impl_trait : Option < Span > ,
60
+ outer_impl_trait : Option < OuterImplTrait > ,
35
61
36
62
// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
37
63
// or `Foo::Bar<impl Trait>`
38
64
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 ,
39
74
}
40
75
41
76
impl < ' 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
+
42
84
fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
43
85
let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
44
86
f ( self ) ;
45
87
self . is_impl_trait_banned = old;
46
88
}
47
89
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 ) ;
50
92
f ( self ) ;
51
93
self . outer_impl_trait = old;
52
94
}
53
95
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
+
54
131
// Mirrors visit::walk_ty, but tracks relevant state
55
132
fn walk_ty ( & mut self , t : & ' a Ty ) {
56
133
match t. node {
57
134
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) )
59
137
}
60
138
TyKind :: Path ( ref qself, ref path) => {
61
139
// We allow these:
@@ -406,22 +484,41 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
406
484
}
407
485
TyKind :: ImplTrait ( _, ref bounds) => {
408
486
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
+ }
411
495
}
412
496
413
497
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
+ }
420
513
}
514
+
421
515
if !bounds. iter ( )
422
516
. any ( |b| if let GenericBound :: Trait ( ..) = * b { true } else { false } ) {
423
517
self . err_handler ( ) . span_err ( ty. span , "at least one trait must be specified" ) ;
424
518
}
519
+
520
+ self . with_impl_trait_in_proj_warning ( true , |this| this. walk_ty ( ty) ) ;
521
+ return ;
425
522
}
426
523
_ => { }
427
524
}
@@ -606,18 +703,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
606
703
GenericArg :: Const ( ..) => ParamKindOrd :: Const ,
607
704
} , arg. span ( ) , None )
608
705
} ) , GenericPosition :: Arg , generic_args. span ( ) ) ;
706
+
609
707
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
610
708
// are allowed to contain nested `impl Trait`.
611
709
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) ;
613
711
} ) ;
614
712
}
615
713
GenericArgs :: Parenthesized ( ref data) => {
616
714
walk_list ! ( self , visit_ty, & data. inputs) ;
617
715
if let Some ( ref type_) = data. output {
618
716
// `-> Foo` syntax is essentially an associated type binding,
619
717
// 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_) ) ;
621
719
}
622
720
}
623
721
}
@@ -719,6 +817,8 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
719
817
has_global_allocator : false ,
720
818
outer_impl_trait : None ,
721
819
is_impl_trait_banned : false ,
820
+ warning_period_57979_didnt_record_next_impl_trait : false ,
821
+ warning_period_57979_impl_trait_in_proj : false ,
722
822
} ;
723
823
visit:: walk_crate ( & mut validator, krate) ;
724
824
0 commit comments