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 # Set of variable that are set (assigned)
92 readable attr _set_variables
: HashSet[Variable] = new HashSet[Variable]
95 meth is_set
(v
: Variable): Bool
97 return _set_variables
.has
(v
) or (_prev
!= null and _prev
.is_set
(v
))
100 meth sub
: ControlFlowContext
102 return new ControlFlowContext.with_prev
(self)
109 init with_prev
(p
: ControlFlowContext)
113 _already_unreash
= p
.already_unreash
117 ###############################################################################
120 private meth accept_control_flow
(v
: ControlFlowVisitor)
122 accept_abs_syntax_visitor
(v
)
126 redef class AMethPropdef
127 redef meth accept_control_flow
(v
)
129 v
.control_flow_ctx
= new ControlFlowContext
134 redef class AConcreteMethPropdef
135 redef meth accept_control_flow
(v
)
138 if v
.control_flow_ctx
.unreash
== false and method
.signature
.return_type
!= null then
139 v
.error
(self, "Control error: Reached end of function (a 'return' with a value was expected).")
144 redef class AVardeclExpr
145 redef meth accept_control_flow
(v
)
148 if n_expr
!= null then v
.mark_is_set
(variable
)
152 redef class ABlockExpr
153 redef meth accept_control_flow
(v
)
156 if v
.control_flow_ctx
.unreash
and not v
.control_flow_ctx
.already_unreash
then
157 v
.control_flow_ctx
.already_unreash
= true
158 v
.warning
(e
, "Warning: unreachable statement.")
165 redef class AReturnExpr
166 redef meth accept_control_flow
(v
)
169 v
.control_flow_ctx
.unreash
= true
173 redef class ABreakExpr
174 redef meth accept_control_flow
(v
)
177 v
.control_flow_ctx
.unreash
= true
180 redef class AContinueExpr
181 redef meth accept_control_flow
(v
)
184 v
.control_flow_ctx
.unreash
= true
188 redef class AAbortExpr
189 redef meth accept_control_flow
(v
)
192 v
.control_flow_ctx
.unreash
= true
196 redef class AClosureCallExpr
197 redef meth accept_control_flow
(v
)
200 if variable
.closure
.is_break
then v
.control_flow_ctx
.unreash
= true
205 redef meth accept_control_flow
(v
)
209 var old_control_flow_ctx
= v
.control_flow_ctx
210 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
214 if n_else
== null then
215 # Restore control flow ctx since the 'then" block is optional
216 v
.control_flow_ctx
= old_control_flow_ctx
218 # Remember what appens in the 'then'
219 var then_control_flow_ctx
= v
.control_flow_ctx
220 # Reset to execute the 'else'
221 v
.control_flow_ctx
= old_control_flow_ctx
.sub
225 # Merge then and else in the old control_flow
226 old_control_flow_ctx
.unreash
= v
.control_flow_ctx
.unreash
and then_control_flow_ctx
.unreash
228 if v
.control_flow_ctx
.unreash
then v
.control_flow_ctx
= then_control_flow_ctx
229 if then_control_flow_ctx
.unreash
then then_control_flow_ctx
= v
.control_flow_ctx
230 for variable
in v
.control_flow_ctx
.set_variables
do
231 if then_control_flow_ctx
.is_set
(variable
) then
232 old_control_flow_ctx
.set_variables
.add
(variable
)
235 v
.control_flow_ctx
= old_control_flow_ctx
240 class AControlableBlock
242 redef meth accept_control_flow
(v
)
244 # Store old control flow values
245 var old_control_flow_ctx
= v
.control_flow_ctx
246 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
250 # Check control flow if any
251 check_control_flow
(v
)
253 # Restore control flow value since all controlable blocks are optionnal
254 v
.control_flow_ctx
= old_control_flow_ctx
257 private meth check_control_flow
(v
: ControlFlowVisitor) do end
260 redef class AWhileExpr
261 special AControlableBlock
265 special AControlableBlock
269 redef meth accept_control_flow
(v
)
272 v
.check_is_set
(self, variable
)
276 redef class AVarAssignExpr
277 redef meth accept_control_flow
(v
)
280 v
.mark_is_set
(variable
)
284 redef class AVarReassignExpr
285 redef meth accept_control_flow
(v
)
288 v
.check_is_set
(self, variable
)
289 v
.mark_is_set
(variable
)
293 redef class AClosureDecl
294 redef meth accept_control_flow
(v
)
296 if n_expr
!= null then
297 var old_control_flow_ctx
= v
.control_flow_ctx
298 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
302 if v
.control_flow_ctx
.unreash
== false then
303 if variable
.closure
.signature
.return_type
!= null then
304 v
.error
(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
305 else if variable
.closure
.is_break
then
306 v
.error
(self, "Control error: Reached end of break bloc (an 'abort' was expected).")
310 v
.control_flow_ctx
= old_control_flow_ctx
315 redef class AClosureDef
316 special AControlableBlock
317 redef meth accept_control_flow
(v
)
319 for va
in variables
do v
.mark_is_set
(va
)
323 redef meth check_control_flow
(v
)
325 if v
.control_flow_ctx
.unreash
== false then
326 if closure
.signature
.return_type
!= null then
327 v
.error
(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
328 else if closure
.is_break
then
329 v
.error
(self, "Control error: Reached end of break bloc (a 'break' was expected).")
335 redef class AOnceExpr
336 redef meth accept_control_flow
(v
)
338 if v
.once_count
> 0 then
339 v
.warning
(self, "Useless once in a once expression.")
341 v
.once_count
= v
.once_count
+ 1
345 v
.once_count
= v
.once_count
- 1