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