Remove specific flow analysis for params and forvardecls
[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 redef class Variable
33 # Is the variable must be set before being used ?
34 meth must_be_set: Bool do return false
35 end
36
37 redef class VarVariable
38 redef meth must_be_set do return true
39 end
40
41
42
43 # Control flow visitor
44 # * Check reachability in methods
45 # * Associate breaks and continues
46 # * Check some other warning
47 private class ControlFlowVisitor
48 special AbsSyntaxVisitor
49 redef meth visit(n)
50 do
51 if n != null then n.accept_control_flow(self)
52 end
53
54 # Number of nested once
55 readable writable attr _once_count: Int
56
57 # Current knowledge about variables types
58 readable writable attr _control_flow_ctx: ControlFlowContext
59
60 meth check_is_set(n: PNode, v: Variable)
61 do
62 if v.must_be_set and not control_flow_ctx.is_set(v) then
63 error(n, "Error: variable '{v}' is possibly unset.")
64 var cfc = control_flow_ctx
65 while cfc != null do
66 print("cfc: " + cfc.set_variables.join(" "))
67 cfc = cfc.prev
68 end
69 end
70 end
71
72 meth mark_is_set(v: Variable)
73 do
74 control_flow_ctx.set_variables.add(v)
75 end
76
77 init(tc, m) do super
78 end
79
80 private class ControlFlowContext
81 # Previous control flow context if any
82 readable attr _prev: ControlFlowContext
83
84 # Is a return met?
85 readable writable attr _has_return: Bool
86
87 # Is a control flow break met? (return, break, continue)
88 readable writable attr _unreash: Bool
89
90 # Is a control flow already broken?
91 # Used to avoid repeating the same error message
92 readable writable attr _already_unreash: Bool
93
94 # Current controlable block (for or while)
95 readable writable attr _base_block: AControlableBlock
96
97 # Set of variable that are set (assigned)
98 readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
99
100 # Is a variable set?
101 meth is_set(v: Variable): Bool
102 do
103 return _set_variables.has(v) or (_prev != null and _prev.is_set(v))
104 end
105
106 meth sub: ControlFlowContext
107 do
108 return new ControlFlowContext.with_prev(self)
109 end
110
111 init
112 do
113 end
114
115 init with_prev(p: ControlFlowContext)
116 do
117 _prev = p
118 _has_return = p.has_return
119 _unreash = p.unreash
120 _already_unreash = p.already_unreash
121 _base_block = p.base_block
122 end
123 end
124
125 ###############################################################################
126
127 redef class PNode
128 private meth accept_control_flow(v: ControlFlowVisitor)
129 do
130 accept_abs_syntax_visitor(v)
131 end
132 end
133
134 redef class AMethPropdef
135 redef meth accept_control_flow(v)
136 do
137 v.control_flow_ctx = new ControlFlowContext
138 super
139 end
140 end
141
142 redef class AConcreteMethPropdef
143 redef meth accept_control_flow(v)
144 do
145 super
146 if v.control_flow_ctx.has_return == false and method.signature.return_type != null then
147 v.error(self, "Control error: Reached end of function.")
148 end
149 end
150 end
151
152 redef class AVardeclExpr
153 redef meth accept_control_flow(v)
154 do
155 super
156 if n_expr != null then v.mark_is_set(variable)
157 end
158 end
159
160 redef class ABlockExpr
161 redef meth accept_control_flow(v)
162 do
163 for e in n_expr do
164 if v.control_flow_ctx.unreash and not v.control_flow_ctx.already_unreash then
165 v.control_flow_ctx.already_unreash = true
166 v.warning(e, "Warning: unreachable statement.")
167 end
168 v.visit(e)
169 end
170 end
171 end
172
173 redef class AReturnExpr
174 redef meth accept_control_flow(v)
175 do
176 super
177 v.control_flow_ctx.has_return = true
178 v.control_flow_ctx.unreash = true
179 end
180 end
181
182 class ABlockControler
183 special PExpr
184 readable attr _block: AControlableBlock
185 end
186
187 redef class ABreakExpr
188 special ABlockControler
189 redef meth accept_control_flow(v)
190 do
191 super
192 var block = v.control_flow_ctx.base_block
193 if block == null then
194 v.error(self, "Syntax Error: 'break' statment outside block.")
195 return
196 end
197 _block = block
198 v.control_flow_ctx.unreash = true
199 end
200 end
201 redef class AContinueExpr
202 special ABlockControler
203 redef meth accept_control_flow(v)
204 do
205 super
206 var block = v.control_flow_ctx.base_block
207 if block == null then
208 v.error(self, "Syntax Error: 'continue' outside block.")
209 return
210 end
211 _block = block
212 v.control_flow_ctx.unreash = true
213 end
214 end
215
216 redef class AAbortExpr
217 redef meth accept_control_flow(v)
218 do
219 super
220 v.control_flow_ctx.has_return = true
221 v.control_flow_ctx.unreash = true
222 end
223 end
224
225 redef class AIfExpr
226 redef meth accept_control_flow(v)
227 do
228 v.visit(n_expr)
229
230 var old_control_flow_ctx = v.control_flow_ctx
231 v.control_flow_ctx = v.control_flow_ctx.sub
232
233 v.visit(n_then)
234
235 if n_else == null then
236 # Restore control flow ctx since the 'then" block is optional
237 v.control_flow_ctx = old_control_flow_ctx
238 else
239 # Remember what appens in the 'then'
240 var then_control_flow_ctx = v.control_flow_ctx
241 # Reset to execute the 'else'
242 v.control_flow_ctx = old_control_flow_ctx.sub
243
244 v.visit(n_else)
245
246 # Merge then and else in the old control_flow
247 old_control_flow_ctx.has_return = v.control_flow_ctx.has_return and then_control_flow_ctx.has_return
248 old_control_flow_ctx.unreash = v.control_flow_ctx.unreash and then_control_flow_ctx.unreash
249
250 if v.control_flow_ctx.unreash then v.control_flow_ctx = then_control_flow_ctx
251 if then_control_flow_ctx.unreash then then_control_flow_ctx = v.control_flow_ctx
252 for variable in v.control_flow_ctx.set_variables do
253 if then_control_flow_ctx.is_set(variable) then
254 old_control_flow_ctx.set_variables.add(variable)
255 end
256 end
257 v.control_flow_ctx = old_control_flow_ctx
258 end
259 end
260 end
261
262 class AControlableBlock
263 special PExpr
264 redef meth accept_control_flow(v)
265 do
266 # Store old control flow values
267 var old_control_flow_ctx = v.control_flow_ctx
268 v.control_flow_ctx = v.control_flow_ctx.sub
269
270 # Register the block
271 v.control_flow_ctx.base_block = self
272
273 super
274
275 # Restore control flow value since all controlable blocks are optionnal
276 v.control_flow_ctx = old_control_flow_ctx
277 end
278 end
279
280 redef class AWhileExpr
281 special AControlableBlock
282 end
283
284 redef class AForExpr
285 special AControlableBlock
286 end
287
288 redef class AVarExpr
289 redef meth accept_control_flow(v)
290 do
291 super
292 v.check_is_set(self, variable)
293 end
294 end
295
296 redef class AVarAssignExpr
297 redef meth accept_control_flow(v)
298 do
299 super
300 v.mark_is_set(variable)
301 end
302 end
303
304 redef class AVarReassignExpr
305 redef meth accept_control_flow(v)
306 do
307 super
308 v.check_is_set(self, variable)
309 v.mark_is_set(variable)
310 end
311 end
312
313
314 redef class AOnceExpr
315 redef meth accept_control_flow(v)
316 do
317 if v.once_count > 0 then
318 v.warning(self, "Useless once in a once expression.")
319 end
320 v.once_count = v.once_count + 1
321
322 super
323
324 v.once_count = v.once_count - 1
325 end
326 end
327