Rename methods named 'with'.
[nit.git] / src / syntax / control_flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Analysis control flow in property bodies, statements and expressions
18 package control_flow
19
20 import syntax_base
21
22 redef class MMSrcModule
23 # Walk trough the module and type statments and expressions
24 # Require than supermodules are processed
25 meth do_control_flow(tc: ToolContext)
26 do
27 var tv = new ControlFlowVisitor(tc, self)
28 tv.visit(node)
29 end
30 end
31
32 # Control flow visitor
33 # * Check reachability in methods
34 # * Associate breaks and continues
35 # * Check some other warning
36 private class ControlFlowVisitor
37 special AbsSyntaxVisitor
38 redef meth visit(n)
39 do
40 if n != null then n.accept_control_flow(self)
41 end
42
43 # Number of nested once
44 readable writable attr _once_count: Int
45
46 # Current knowledge about variables types
47 readable writable attr _control_flow_ctx: ControlFlowContext
48
49 meth check_is_set(n: PNode, v: Variable)
50 do
51 if not control_flow_ctx.is_set(v) then
52 error(n, "Error: variable '{v}' is possibly unset.")
53 var cfc = control_flow_ctx
54 while cfc != null do
55 print("cfc: " + cfc.set_variables.join(" "))
56 cfc = cfc.prev
57 end
58 end
59 end
60
61 meth mark_is_set(v: Variable)
62 do
63 control_flow_ctx.set_variables.add(v)
64 end
65
66 init(tc, m) do super
67 end
68
69 private class ControlFlowContext
70 # Previous control flow context if any
71 readable attr _prev: ControlFlowContext
72
73 # Is a return met?
74 readable writable attr _has_return: Bool
75
76 # Is a control flow break met? (return, break, continue)
77 readable writable attr _unreash: Bool
78
79 # Is a control flow already broken?
80 # Used to avoid repeating the same error message
81 readable writable attr _already_unreash: Bool
82
83 # Current controlable block (for or while)
84 readable writable attr _base_block: AControlableBlock
85
86 # Set of variable that are set (assigned)
87 readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
88
89 # Is a variable set?
90 meth is_set(v: Variable): Bool
91 do
92 return _set_variables.has(v) or (_prev != null and _prev.is_set(v))
93 end
94
95 meth sub: ControlFlowContext
96 do
97 return new ControlFlowContext.with_prev(self)
98 end
99
100 init
101 do
102 end
103
104 init with_prev(p: ControlFlowContext)
105 do
106 _prev = p
107 _has_return = p.has_return
108 _unreash = p.unreash
109 _already_unreash = p.already_unreash
110 _base_block = p.base_block
111 end
112 end
113
114 ###############################################################################
115
116 redef class PNode
117 private meth accept_control_flow(v: ControlFlowVisitor)
118 do
119 accept_abs_syntax_visitor(v)
120 end
121 end
122
123 redef class AMethPropdef
124 redef meth accept_control_flow(v)
125 do
126 v.control_flow_ctx = new ControlFlowContext
127 super
128 end
129 end
130
131 redef class AConcreteMethPropdef
132 redef meth accept_control_flow(v)
133 do
134 super
135 if v.control_flow_ctx.has_return == false and method.signature.return_type != null then
136 v.error(self, "Control error: Reached end of function.")
137 end
138 end
139 end
140
141 redef class PParam
142 redef meth accept_control_flow(v)
143 do
144 super
145 v.mark_is_set(variable)
146 end
147 end
148
149 redef class AVardeclExpr
150 redef meth accept_control_flow(v)
151 do
152 super
153 if n_expr != null then v.mark_is_set(variable)
154 end
155 end
156
157 redef class ABlockExpr
158 redef meth accept_control_flow(v)
159 do
160 for e in n_expr do
161 if v.control_flow_ctx.unreash and not v.control_flow_ctx.already_unreash then
162 v.control_flow_ctx.already_unreash = true
163 v.warning(e, "Warning: unreachable statement.")
164 end
165 v.visit(e)
166 end
167 end
168 end
169
170 redef class AReturnExpr
171 redef meth accept_control_flow(v)
172 do
173 super
174 v.control_flow_ctx.has_return = true
175 v.control_flow_ctx.unreash = true
176 end
177 end
178
179 class ABlockControler
180 special PExpr
181 readable attr _block: AControlableBlock
182 end
183
184 redef class ABreakExpr
185 special ABlockControler
186 redef meth accept_control_flow(v)
187 do
188 super
189 var block = v.control_flow_ctx.base_block
190 if block == null then
191 v.error(self, "Syntax Error: 'break' statment outside block.")
192 return
193 end
194 _block = block
195 v.control_flow_ctx.unreash = true
196 end
197 end
198 redef class AContinueExpr
199 special ABlockControler
200 redef meth accept_control_flow(v)
201 do
202 super
203 var block = v.control_flow_ctx.base_block
204 if block == null then
205 v.error(self, "Syntax Error: 'continue' outside block.")
206 return
207 end
208 _block = block
209 v.control_flow_ctx.unreash = true
210 end
211 end
212
213 redef class AAbortExpr
214 redef meth accept_control_flow(v)
215 do
216 super
217 v.control_flow_ctx.has_return = true
218 v.control_flow_ctx.unreash = true
219 end
220 end
221
222 redef class AIfExpr
223 redef meth accept_control_flow(v)
224 do
225 v.visit(n_expr)
226
227 var old_control_flow_ctx = v.control_flow_ctx
228 v.control_flow_ctx = v.control_flow_ctx.sub
229
230 v.visit(n_then)
231
232 if n_else == null then
233 # Restore control flow ctx since the 'then" block is optional
234 v.control_flow_ctx = old_control_flow_ctx
235 else
236 # Remember what appens in the 'then'
237 var then_control_flow_ctx = v.control_flow_ctx
238 # Reset to execute the 'else'
239 v.control_flow_ctx = old_control_flow_ctx.sub
240
241 v.visit(n_else)
242
243 # Merge then and else in the old control_flow
244 old_control_flow_ctx.has_return = v.control_flow_ctx.has_return and then_control_flow_ctx.has_return
245 old_control_flow_ctx.unreash = v.control_flow_ctx.unreash and then_control_flow_ctx.unreash
246
247 if v.control_flow_ctx.unreash then v.control_flow_ctx = then_control_flow_ctx
248 if then_control_flow_ctx.unreash then then_control_flow_ctx = v.control_flow_ctx
249 for variable in v.control_flow_ctx.set_variables do
250 if then_control_flow_ctx.is_set(variable) then
251 old_control_flow_ctx.set_variables.add(variable)
252 end
253 end
254 v.control_flow_ctx = old_control_flow_ctx
255 end
256 end
257 end
258
259 class AControlableBlock
260 special PExpr
261 redef meth accept_control_flow(v)
262 do
263 # Store old control flow values
264 var old_control_flow_ctx = v.control_flow_ctx
265 v.control_flow_ctx = v.control_flow_ctx.sub
266
267 # Register the block
268 v.control_flow_ctx.base_block = self
269
270 super
271
272 # Restore control flow value since all controlable blocks are optionnal
273 v.control_flow_ctx = old_control_flow_ctx
274 end
275 end
276
277 redef class AWhileExpr
278 special AControlableBlock
279 end
280
281 redef class AForExpr
282 special AControlableBlock
283 end
284
285 redef class AForVardeclExpr
286 redef meth accept_control_flow(v)
287 do
288 super
289 v.mark_is_set(variable)
290 end
291 end
292
293
294 redef class AVarExpr
295 redef meth accept_control_flow(v)
296 do
297 super
298 v.check_is_set(self, variable)
299 end
300 end
301
302 redef class AVarAssignExpr
303 redef meth accept_control_flow(v)
304 do
305 super
306 v.mark_is_set(variable)
307 end
308 end
309
310 redef class AVarReassignExpr
311 redef meth accept_control_flow(v)
312 do
313 super
314 v.check_is_set(self, variable)
315 v.mark_is_set(variable)
316 end
317 end
318
319
320 redef class AOnceExpr
321 redef meth accept_control_flow(v)
322 do
323 if v.once_count > 0 then
324 v.warning(self, "Useless once in a once expression.")
325 end
326 v.once_count = v.once_count + 1
327
328 super
329
330 v.once_count = v.once_count - 1
331 end
332 end
333