13
13
14
14
use ast_map;
15
15
use session:: Session ;
16
- use middle:: def:: { DefStatic , DefConst , DefAssociatedConst , DefMap } ;
16
+ use middle:: def:: { DefStatic , DefConst , DefAssociatedConst , DefVariant , DefMap } ;
17
+ use util:: nodemap:: NodeMap ;
17
18
18
19
use syntax:: { ast, ast_util} ;
19
20
use syntax:: codemap:: Span ;
20
21
use syntax:: visit:: Visitor ;
21
22
use syntax:: visit;
22
23
24
+ use std:: cell:: RefCell ;
25
+
23
26
struct CheckCrateVisitor < ' a , ' ast : ' a > {
24
27
sess : & ' a Session ,
25
28
def_map : & ' a DefMap ,
26
- ast_map : & ' a ast_map:: Map < ' ast >
29
+ ast_map : & ' a ast_map:: Map < ' ast > ,
30
+ // `discriminant_map` is a cache that associates the `NodeId`s of local
31
+ // variant definitions with the discriminant expression that applies to
32
+ // each one. If the variant uses the default values (starting from `0`),
33
+ // then `None` is stored.
34
+ discriminant_map : RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
27
35
}
28
36
29
- impl < ' v , ' a , ' ast > Visitor < ' v > for CheckCrateVisitor < ' a , ' ast > {
30
- fn visit_item ( & mut self , it : & ast:: Item ) {
37
+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckCrateVisitor < ' a , ' ast > {
38
+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
31
39
match it. node {
32
- ast:: ItemStatic ( _ , _ , ref expr ) |
33
- ast:: ItemConst ( _ , ref expr ) => {
40
+ ast:: ItemStatic ( .. ) |
41
+ ast:: ItemConst ( .. ) => {
34
42
let mut recursion_visitor =
35
43
CheckItemRecursionVisitor :: new ( self , & it. span ) ;
36
44
recursion_visitor. visit_item ( it) ;
37
- visit:: walk_expr ( self , & * expr)
38
45
} ,
39
- _ => visit:: walk_item ( self , it)
46
+ ast:: ItemEnum ( ref enum_def, ref generics) => {
47
+ // We could process the whole enum, but handling the variants
48
+ // with discriminant expressions one by one gives more specific,
49
+ // less redundant output.
50
+ for variant in & enum_def. variants {
51
+ if let Some ( _) = variant. node . disr_expr {
52
+ let mut recursion_visitor =
53
+ CheckItemRecursionVisitor :: new ( self , & variant. span ) ;
54
+ recursion_visitor. populate_enum_discriminants ( enum_def) ;
55
+ recursion_visitor. visit_variant ( variant, generics) ;
56
+ }
57
+ }
58
+ }
59
+ _ => { }
40
60
}
61
+ visit:: walk_item ( self , it)
41
62
}
42
63
43
- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
64
+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
44
65
match ti. node {
45
66
ast:: ConstTraitItem ( _, ref default) => {
46
- if let Some ( ref expr ) = * default {
67
+ if let Some ( _ ) = * default {
47
68
let mut recursion_visitor =
48
69
CheckItemRecursionVisitor :: new ( self , & ti. span ) ;
49
70
recursion_visitor. visit_trait_item ( ti) ;
50
- visit:: walk_expr ( self , & * expr)
51
71
}
52
72
}
53
- _ => visit :: walk_trait_item ( self , ti )
73
+ _ => { }
54
74
}
75
+ visit:: walk_trait_item ( self , ti)
55
76
}
56
77
57
- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
78
+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
58
79
match ii. node {
59
- ast:: ConstImplItem ( _ , ref expr ) => {
80
+ ast:: ConstImplItem ( .. ) => {
60
81
let mut recursion_visitor =
61
82
CheckItemRecursionVisitor :: new ( self , & ii. span ) ;
62
83
recursion_visitor. visit_impl_item ( ii) ;
63
- visit:: walk_expr ( self , & * expr)
64
84
}
65
- _ => visit :: walk_impl_item ( self , ii )
85
+ _ => { }
66
86
}
87
+ visit:: walk_impl_item ( self , ii)
67
88
}
68
89
}
69
90
70
91
pub fn check_crate < ' ast > ( sess : & Session ,
71
- krate : & ast:: Crate ,
92
+ krate : & ' ast ast:: Crate ,
72
93
def_map : & DefMap ,
73
94
ast_map : & ast_map:: Map < ' ast > ) {
74
95
let mut visitor = CheckCrateVisitor {
75
96
sess : sess,
76
97
def_map : def_map,
77
- ast_map : ast_map
98
+ ast_map : ast_map,
99
+ discriminant_map : RefCell :: new ( NodeMap ( ) ) ,
78
100
} ;
79
101
visit:: walk_crate ( & mut visitor, krate) ;
80
102
sess. abort_if_errors ( ) ;
@@ -85,53 +107,120 @@ struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
85
107
sess : & ' a Session ,
86
108
ast_map : & ' a ast_map:: Map < ' ast > ,
87
109
def_map : & ' a DefMap ,
88
- idstack : Vec < ast:: NodeId >
110
+ discriminant_map : & ' a RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
111
+ idstack : Vec < ast:: NodeId > ,
89
112
}
90
113
91
114
impl < ' a , ' ast : ' a > CheckItemRecursionVisitor < ' a , ' ast > {
92
- fn new ( v : & CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
115
+ fn new ( v : & ' a CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
93
116
-> CheckItemRecursionVisitor < ' a , ' ast > {
94
117
CheckItemRecursionVisitor {
95
118
root_span : span,
96
119
sess : v. sess ,
97
120
ast_map : v. ast_map ,
98
121
def_map : v. def_map ,
99
- idstack : Vec :: new ( )
122
+ discriminant_map : & v. discriminant_map ,
123
+ idstack : Vec :: new ( ) ,
100
124
}
101
125
}
102
126
fn with_item_id_pushed < F > ( & mut self , id : ast:: NodeId , f : F )
103
127
where F : Fn ( & mut Self ) {
104
- if self . idstack . iter ( ) . any ( |x| x == & ( id ) ) {
128
+ if self . idstack . iter ( ) . any ( |x| * x == id ) {
105
129
span_err ! ( self . sess, * self . root_span, E0265 , "recursive constant" ) ;
106
130
return ;
107
131
}
108
132
self . idstack . push ( id) ;
109
133
f ( self ) ;
110
134
self . idstack . pop ( ) ;
111
135
}
136
+ // If a variant has an expression specifying its discriminant, then it needs
137
+ // to be checked just like a static or constant. However, if there are more
138
+ // variants with no explicitly specified discriminant, those variants will
139
+ // increment the same expression to get their values.
140
+ //
141
+ // So for every variant, we need to track whether there is an expression
142
+ // somewhere in the enum definition that controls its discriminant. We do
143
+ // this by starting from the end and searching backward.
144
+ fn populate_enum_discriminants ( & self , enum_definition : & ' ast ast:: EnumDef ) {
145
+ // Get the map, and return if we already processed this enum or if it
146
+ // has no variants.
147
+ let mut discriminant_map = self . discriminant_map . borrow_mut ( ) ;
148
+ match enum_definition. variants . first ( ) {
149
+ None => { return ; }
150
+ Some ( variant) if discriminant_map. contains_key ( & variant. node . id ) => {
151
+ return ;
152
+ }
153
+ _ => { }
154
+ }
155
+
156
+ // Go through all the variants.
157
+ let mut variant_stack: Vec < ast:: NodeId > = Vec :: new ( ) ;
158
+ for variant in enum_definition. variants . iter ( ) . rev ( ) {
159
+ variant_stack. push ( variant. node . id ) ;
160
+ // When we find an expression, every variant currently on the stack
161
+ // is affected by that expression.
162
+ if let Some ( ref expr) = variant. node . disr_expr {
163
+ for id in & variant_stack {
164
+ discriminant_map. insert ( * id, Some ( expr) ) ;
165
+ }
166
+ variant_stack. clear ( )
167
+ }
168
+ }
169
+ // If we are at the top, that always starts at 0, so any variant on the
170
+ // stack has a default value and does not need to be checked.
171
+ for id in & variant_stack {
172
+ discriminant_map. insert ( * id, None ) ;
173
+ }
174
+ }
112
175
}
113
176
114
- impl < ' a , ' ast , ' v > Visitor < ' v > for CheckItemRecursionVisitor < ' a , ' ast > {
115
- fn visit_item ( & mut self , it : & ast:: Item ) {
177
+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckItemRecursionVisitor < ' a , ' ast > {
178
+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
116
179
self . with_item_id_pushed ( it. id , |v| visit:: walk_item ( v, it) ) ;
117
180
}
118
181
119
- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
182
+ fn visit_enum_def ( & mut self , enum_definition : & ' ast ast:: EnumDef ,
183
+ generics : & ' ast ast:: Generics ) {
184
+ self . populate_enum_discriminants ( enum_definition) ;
185
+ visit:: walk_enum_def ( self , enum_definition, generics) ;
186
+ }
187
+
188
+ fn visit_variant ( & mut self , variant : & ' ast ast:: Variant ,
189
+ _: & ' ast ast:: Generics ) {
190
+ let variant_id = variant. node . id ;
191
+ let maybe_expr;
192
+ if let Some ( get_expr) = self . discriminant_map . borrow ( ) . get ( & variant_id) {
193
+ // This is necessary because we need to let the `discriminant_map`
194
+ // borrow fall out of scope, so that we can reborrow farther down.
195
+ maybe_expr = ( * get_expr) . clone ( ) ;
196
+ } else {
197
+ self . sess . span_bug ( variant. span ,
198
+ "`check_static_recursion` attempted to visit \
199
+ variant with unknown discriminant")
200
+ }
201
+ // If `maybe_expr` is `None`, that's because no discriminant is
202
+ // specified that affects this variant. Thus, no risk of recursion.
203
+ if let Some ( expr) = maybe_expr {
204
+ self . with_item_id_pushed ( expr. id , |v| visit:: walk_expr ( v, expr) ) ;
205
+ }
206
+ }
207
+
208
+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
120
209
self . with_item_id_pushed ( ti. id , |v| visit:: walk_trait_item ( v, ti) ) ;
121
210
}
122
211
123
- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
212
+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
124
213
self . with_item_id_pushed ( ii. id , |v| visit:: walk_impl_item ( v, ii) ) ;
125
214
}
126
215
127
- fn visit_expr ( & mut self , e : & ast:: Expr ) {
216
+ fn visit_expr ( & mut self , e : & ' ast ast:: Expr ) {
128
217
match e. node {
129
218
ast:: ExprPath ( ..) => {
130
219
match self . def_map . borrow ( ) . get ( & e. id ) . map ( |d| d. base_def ) {
131
220
Some ( DefStatic ( def_id, _) ) |
132
221
Some ( DefAssociatedConst ( def_id, _) ) |
133
- Some ( DefConst ( def_id) ) if
134
- ast_util:: is_local ( def_id) => {
222
+ Some ( DefConst ( def_id) )
223
+ if ast_util:: is_local ( def_id) => {
135
224
match self . ast_map . get ( def_id. node ) {
136
225
ast_map:: NodeItem ( item) =>
137
226
self . visit_item ( item) ,
@@ -141,11 +230,28 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
141
230
self . visit_impl_item ( item) ,
142
231
ast_map:: NodeForeignItem ( _) => { } ,
143
232
_ => {
144
- span_err ! ( self . sess, e. span, E0266 ,
145
- "expected item, found {}" ,
146
- self . ast_map. node_to_string( def_id. node) ) ;
147
- return ;
148
- } ,
233
+ self . sess . span_bug (
234
+ e. span ,
235
+ & format ! ( "expected item, found {}" ,
236
+ self . ast_map. node_to_string( def_id. node) ) ) ;
237
+ }
238
+ }
239
+ }
240
+ // For variants, we only want to check expressions that
241
+ // affect the specific variant used, but we need to check
242
+ // the whole enum definition to see what expression that
243
+ // might be (if any).
244
+ Some ( DefVariant ( enum_id, variant_id, false ) )
245
+ if ast_util:: is_local ( enum_id) => {
246
+ if let ast:: ItemEnum ( ref enum_def, ref generics) =
247
+ self . ast_map . expect_item ( enum_id. local_id ( ) ) . node {
248
+ self . populate_enum_discriminants ( enum_def) ;
249
+ let variant = self . ast_map . expect_variant ( variant_id. local_id ( ) ) ;
250
+ self . visit_variant ( variant, generics) ;
251
+ } else {
252
+ self . sess . span_bug ( e. span ,
253
+ "`check_static_recursion` found \
254
+ non-enum in DefVariant") ;
149
255
}
150
256
}
151
257
_ => ( )
0 commit comments