1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2008 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Analysis control flow in property bodies, statements and expressions
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)
27 var tv
= new ControlFlowVisitor(tc
, self)
33 # Is the variable must be set before being used ?
34 meth must_be_set
: Bool do return false
37 redef class VarVariable
38 redef meth must_be_set
do return true
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
51 if n
!= null then n
.accept_control_flow
(self)
54 # Number of nested once
55 readable writable attr _once_count
: Int = 0
57 # Current knowledge about variables types
58 readable writable attr _control_flow_ctx
: ControlFlowContext
60 meth check_is_set
(n
: PNode, v
: Variable)
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
66 print
("cfc: " + cfc
.set_variables
.join
(" "))
72 meth mark_is_set
(v
: Variable)
74 control_flow_ctx
.set_variables
.add
(v
)
80 private class ControlFlowContext
81 # Previous control flow context if any
82 readable attr _prev
: ControlFlowContext
84 # Is a control flow break met? (return, break, continue)
85 readable writable attr _unreash
: Bool = false
87 # Is a control flow already broken?
88 # Used to avoid repeating the same error message
89 readable writable attr _already_unreash
: Bool = false
91 # Current controlable block (for or while)
92 readable writable attr _base_block
: PNode = null
94 # Set of variable that are set (assigned)
95 readable attr _set_variables
: HashSet[Variable] = new HashSet[Variable]
98 meth is_set
(v
: Variable): Bool
100 return _set_variables
.has
(v
) or (_prev
!= null and _prev
.is_set
(v
))
103 meth sub
: ControlFlowContext
105 return new ControlFlowContext.with_prev
(self)
112 init with_prev
(p
: ControlFlowContext)
116 _already_unreash
= p
.already_unreash
117 _base_block
= p
.base_block
121 ###############################################################################
124 private meth accept_control_flow
(v
: ControlFlowVisitor)
126 accept_abs_syntax_visitor
(v
)
130 redef class AMethPropdef
131 redef meth accept_control_flow
(v
)
133 v
.control_flow_ctx
= new ControlFlowContext
138 redef class AConcreteMethPropdef
139 redef meth accept_control_flow
(v
)
142 if v
.control_flow_ctx
.unreash
== false and method
.signature
.return_type
!= null then
143 v
.error
(self, "Control error: Reached end of function (a 'return' with a value was expected).")
148 redef class AVardeclExpr
149 redef meth accept_control_flow
(v
)
152 if n_expr
!= null then v
.mark_is_set
(variable
)
156 redef class ABlockExpr
157 redef meth accept_control_flow
(v
)
160 if v
.control_flow_ctx
.unreash
and not v
.control_flow_ctx
.already_unreash
then
161 v
.control_flow_ctx
.already_unreash
= true
162 v
.warning
(e
, "Warning: unreachable statement.")
169 redef class AReturnExpr
170 redef meth accept_control_flow
(v
)
173 v
.control_flow_ctx
.unreash
= true
177 class ABlockControler
179 readable attr _block
: PNode
182 redef class ABreakExpr
183 special ABlockControler
184 redef meth accept_control_flow
(v
)
187 var block
= v
.control_flow_ctx
.base_block
188 if block
== null then
189 v
.error
(self, "Syntax Error: 'break' statment outside block.")
193 v
.control_flow_ctx
.unreash
= true
196 redef class AContinueExpr
197 special ABlockControler
198 redef meth accept_control_flow
(v
)
201 var block
= v
.control_flow_ctx
.base_block
202 if block
== null then
203 v
.error
(self, "Syntax Error: 'continue' outside block.")
207 v
.control_flow_ctx
.unreash
= true
211 redef class AAbortExpr
212 redef meth accept_control_flow
(v
)
215 v
.control_flow_ctx
.unreash
= true
219 redef class AClosureCallExpr
220 redef meth accept_control_flow
(v
)
223 if variable
.closure
.is_break
then v
.control_flow_ctx
.unreash
= true
228 redef meth accept_control_flow
(v
)
232 var old_control_flow_ctx
= v
.control_flow_ctx
233 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
237 if n_else
== null then
238 # Restore control flow ctx since the 'then" block is optional
239 v
.control_flow_ctx
= old_control_flow_ctx
241 # Remember what appens in the 'then'
242 var then_control_flow_ctx
= v
.control_flow_ctx
243 # Reset to execute the 'else'
244 v
.control_flow_ctx
= old_control_flow_ctx
.sub
248 # Merge then and else in the old control_flow
249 old_control_flow_ctx
.unreash
= v
.control_flow_ctx
.unreash
and then_control_flow_ctx
.unreash
251 if v
.control_flow_ctx
.unreash
then v
.control_flow_ctx
= then_control_flow_ctx
252 if then_control_flow_ctx
.unreash
then then_control_flow_ctx
= v
.control_flow_ctx
253 for variable
in v
.control_flow_ctx
.set_variables
do
254 if then_control_flow_ctx
.is_set
(variable
) then
255 old_control_flow_ctx
.set_variables
.add
(variable
)
258 v
.control_flow_ctx
= old_control_flow_ctx
263 class AControlableBlock
265 redef meth accept_control_flow
(v
)
267 # Store old control flow values
268 var old_control_flow_ctx
= v
.control_flow_ctx
269 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
272 v
.control_flow_ctx
.base_block
= self
276 # Check control flow if any
277 check_control_flow
(v
)
279 # Restore control flow value since all controlable blocks are optionnal
280 v
.control_flow_ctx
= old_control_flow_ctx
283 private meth check_control_flow
(v
: ControlFlowVisitor) do end
286 redef class AWhileExpr
287 special AControlableBlock
291 special AControlableBlock
295 redef meth accept_control_flow
(v
)
298 v
.check_is_set
(self, variable
)
302 redef class AVarAssignExpr
303 redef meth accept_control_flow
(v
)
306 v
.mark_is_set
(variable
)
310 redef class AVarReassignExpr
311 redef meth accept_control_flow
(v
)
314 v
.check_is_set
(self, variable
)
315 v
.mark_is_set
(variable
)
319 redef class AClosureDecl
320 redef meth accept_control_flow
(v
)
322 if n_expr
!= null then
323 var old_control_flow_ctx
= v
.control_flow_ctx
324 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
326 v
.control_flow_ctx
.base_block
= n_expr
330 if v
.control_flow_ctx
.unreash
== false then
331 if variable
.closure
.signature
.return_type
!= null then
332 v
.error
(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
333 else if variable
.closure
.is_break
then
334 v
.error
(self, "Control error: Reached end of break bloc (an 'abort' was expected).")
338 v
.control_flow_ctx
= old_control_flow_ctx
343 redef class AClosureDef
344 special AControlableBlock
345 redef meth accept_control_flow
(v
)
347 for va
in variables
do v
.mark_is_set
(va
)
351 redef meth check_control_flow
(v
)
353 if v
.control_flow_ctx
.unreash
== false then
354 if closure
.signature
.return_type
!= null then
355 v
.error
(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
356 else if closure
.is_break
then
357 v
.error
(self, "Control error: Reached end of break bloc (a 'break' was expected).")
363 redef class AOnceExpr
364 redef meth accept_control_flow
(v
)
366 if v
.once_count
> 0 then
367 v
.warning
(self, "Useless once in a once expression.")
369 v
.once_count
= v
.once_count
+ 1
373 v
.once_count
= v
.once_count
- 1