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
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
85 readable writable attr _has_return
: Bool
87 # Is a control flow break met? (return, break, continue)
88 readable writable attr _unreash
: Bool
90 # Is a control flow already broken?
91 # Used to avoid repeating the same error message
92 readable writable attr _already_unreash
: Bool
94 # Current controlable block (for or while)
95 readable writable attr _base_block
: AControlableBlock
97 # Set of variable that are set (assigned)
98 readable attr _set_variables
: HashSet[Variable] = new HashSet[Variable]
101 meth is_set
(v
: Variable): Bool
103 return _set_variables
.has
(v
) or (_prev
!= null and _prev
.is_set
(v
))
106 meth sub
: ControlFlowContext
108 return new ControlFlowContext.with_prev
(self)
115 init with_prev
(p
: ControlFlowContext)
118 _has_return
= p
.has_return
120 _already_unreash
= p
.already_unreash
121 _base_block
= p
.base_block
125 ###############################################################################
128 private meth accept_control_flow
(v
: ControlFlowVisitor)
130 accept_abs_syntax_visitor
(v
)
134 redef class AMethPropdef
135 redef meth accept_control_flow
(v
)
137 v
.control_flow_ctx
= new ControlFlowContext
142 redef class AConcreteMethPropdef
143 redef meth accept_control_flow
(v
)
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.")
152 redef class AVardeclExpr
153 redef meth accept_control_flow
(v
)
156 if n_expr
!= null then v
.mark_is_set
(variable
)
160 redef class ABlockExpr
161 redef meth accept_control_flow
(v
)
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.")
173 redef class AReturnExpr
174 redef meth accept_control_flow
(v
)
177 v
.control_flow_ctx
.has_return
= true
178 v
.control_flow_ctx
.unreash
= true
182 class ABlockControler
184 readable attr _block
: AControlableBlock
187 redef class ABreakExpr
188 special ABlockControler
189 redef meth accept_control_flow
(v
)
192 var block
= v
.control_flow_ctx
.base_block
193 if block
== null then
194 v
.error
(self, "Syntax Error: 'break' statment outside block.")
198 v
.control_flow_ctx
.unreash
= true
201 redef class AContinueExpr
202 special ABlockControler
203 redef meth accept_control_flow
(v
)
206 var block
= v
.control_flow_ctx
.base_block
207 if block
== null then
208 v
.error
(self, "Syntax Error: 'continue' outside block.")
212 v
.control_flow_ctx
.unreash
= true
216 redef class AAbortExpr
217 redef meth accept_control_flow
(v
)
220 v
.control_flow_ctx
.has_return
= true
221 v
.control_flow_ctx
.unreash
= true
226 redef meth accept_control_flow
(v
)
230 var old_control_flow_ctx
= v
.control_flow_ctx
231 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
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
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
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
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
)
257 v
.control_flow_ctx
= old_control_flow_ctx
262 class AControlableBlock
264 redef meth accept_control_flow
(v
)
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
271 v
.control_flow_ctx
.base_block
= self
275 # Restore control flow value since all controlable blocks are optionnal
276 v
.control_flow_ctx
= old_control_flow_ctx
280 redef class AWhileExpr
281 special AControlableBlock
285 special AControlableBlock
289 redef meth accept_control_flow
(v
)
292 v
.check_is_set
(self, variable
)
296 redef class AVarAssignExpr
297 redef meth accept_control_flow
(v
)
300 v
.mark_is_set
(variable
)
304 redef class AVarReassignExpr
305 redef meth accept_control_flow
(v
)
308 v
.check_is_set
(self, variable
)
309 v
.mark_is_set
(variable
)
314 redef class AOnceExpr
315 redef meth accept_control_flow
(v
)
317 if v
.once_count
> 0 then
318 v
.warning
(self, "Useless once in a once expression.")
320 v
.once_count
= v
.once_count
+ 1
324 v
.once_count
= v
.once_count
- 1