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
: AControlableBlock
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
: AControlableBlock
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
220 redef meth accept_control_flow
(v
)
224 var old_control_flow_ctx
= v
.control_flow_ctx
225 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
229 if n_else
== null then
230 # Restore control flow ctx since the 'then" block is optional
231 v
.control_flow_ctx
= old_control_flow_ctx
233 # Remember what appens in the 'then'
234 var then_control_flow_ctx
= v
.control_flow_ctx
235 # Reset to execute the 'else'
236 v
.control_flow_ctx
= old_control_flow_ctx
.sub
240 # Merge then and else in the old control_flow
241 old_control_flow_ctx
.unreash
= v
.control_flow_ctx
.unreash
and then_control_flow_ctx
.unreash
243 if v
.control_flow_ctx
.unreash
then v
.control_flow_ctx
= then_control_flow_ctx
244 if then_control_flow_ctx
.unreash
then then_control_flow_ctx
= v
.control_flow_ctx
245 for variable
in v
.control_flow_ctx
.set_variables
do
246 if then_control_flow_ctx
.is_set
(variable
) then
247 old_control_flow_ctx
.set_variables
.add
(variable
)
250 v
.control_flow_ctx
= old_control_flow_ctx
255 class AControlableBlock
257 redef meth accept_control_flow
(v
)
259 # Store old control flow values
260 var old_control_flow_ctx
= v
.control_flow_ctx
261 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
264 v
.control_flow_ctx
.base_block
= self
268 # Check control flow if any
269 check_control_flow
(v
)
271 # Restore control flow value since all controlable blocks are optionnal
272 v
.control_flow_ctx
= old_control_flow_ctx
275 private meth check_control_flow
(v
: ControlFlowVisitor) do end
278 redef class AWhileExpr
279 special AControlableBlock
283 special AControlableBlock
287 redef meth accept_control_flow
(v
)
290 v
.check_is_set
(self, variable
)
294 redef class AVarAssignExpr
295 redef meth accept_control_flow
(v
)
298 v
.mark_is_set
(variable
)
302 redef class AVarReassignExpr
303 redef meth accept_control_flow
(v
)
306 v
.check_is_set
(self, variable
)
307 v
.mark_is_set
(variable
)
311 redef class AClosureDef
312 special AControlableBlock
313 redef meth accept_control_flow
(v
)
315 for va
in variables
do v
.mark_is_set
(va
)
319 redef meth check_control_flow
(v
)
321 if v
.control_flow_ctx
.unreash
== false and closure
.signature
.return_type
!= null then
322 v
.error
(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
327 redef class AOnceExpr
328 redef meth accept_control_flow
(v
)
330 if v
.once_count
> 0 then
331 v
.warning
(self, "Useless once in a once expression.")
333 v
.once_count
= v
.once_count
+ 1
337 v
.once_count
= v
.once_count
- 1