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)
32 # Control flow visitor
33 # * Check reachability in methods
34 # * Associate breaks and continues
35 # * Check some other warning
36 private class ControlFlowVisitor
37 special AbsSyntaxVisitor
40 if n
!= null then n
.accept_control_flow
(self)
43 # Number of nested once
44 readable writable attr _once_count
: Int
46 # Current knowledge about variables types
47 readable writable attr _control_flow_ctx
: ControlFlowContext
49 meth check_is_set
(n
: PNode, v
: Variable)
51 if not control_flow_ctx
.is_set
(v
) then
52 error
(n
, "Error: variable '{v}' is possibly unset.")
53 var cfc
= control_flow_ctx
55 print
("cfc: " + cfc
.set_variables
.join
(" "))
61 meth mark_is_set
(v
: Variable)
63 control_flow_ctx
.set_variables
.add
(v
)
69 private class ControlFlowContext
70 # Previous control flow context if any
71 readable attr _prev
: ControlFlowContext
74 readable writable attr _has_return
: Bool
76 # Is a control flow break met? (return, break, continue)
77 readable writable attr _unreash
: Bool
79 # Is a control flow already broken?
80 # Used to avoid repeating the same error message
81 readable writable attr _already_unreash
: Bool
83 # Current controlable block (for or while)
84 readable writable attr _base_block
: AControlableBlock
86 # Set of variable that are set (assigned)
87 readable attr _set_variables
: HashSet[Variable] = new HashSet[Variable]
90 meth is_set
(v
: Variable): Bool
92 return _set_variables
.has
(v
) or (_prev
!= null and _prev
.is_set
(v
))
95 meth sub
: ControlFlowContext
97 return new ControlFlowContext.with_prev
(self)
104 init with_prev
(p
: ControlFlowContext)
107 _has_return
= p
.has_return
109 _already_unreash
= p
.already_unreash
110 _base_block
= p
.base_block
114 ###############################################################################
117 private meth accept_control_flow
(v
: ControlFlowVisitor)
119 accept_abs_syntax_visitor
(v
)
123 redef class AMethPropdef
124 redef meth accept_control_flow
(v
)
126 v
.control_flow_ctx
= new ControlFlowContext
131 redef class AConcreteMethPropdef
132 redef meth accept_control_flow
(v
)
135 if v
.control_flow_ctx
.has_return
== false and method
.signature
.return_type
!= null then
136 v
.error
(self, "Control error: Reached end of function.")
142 redef meth accept_control_flow
(v
)
145 v
.mark_is_set
(variable
)
149 redef class AVardeclExpr
150 redef meth accept_control_flow
(v
)
153 if n_expr
!= null then v
.mark_is_set
(variable
)
157 redef class ABlockExpr
158 redef meth accept_control_flow
(v
)
161 if v
.control_flow_ctx
.unreash
and not v
.control_flow_ctx
.already_unreash
then
162 v
.control_flow_ctx
.already_unreash
= true
163 v
.warning
(e
, "Warning: unreachable statement.")
170 redef class AReturnExpr
171 redef meth accept_control_flow
(v
)
174 v
.control_flow_ctx
.has_return
= true
175 v
.control_flow_ctx
.unreash
= true
179 class ABlockControler
181 readable attr _block
: AControlableBlock
184 redef class ABreakExpr
185 special ABlockControler
186 redef meth accept_control_flow
(v
)
189 var block
= v
.control_flow_ctx
.base_block
190 if block
== null then
191 v
.error
(self, "Syntax Error: 'break' statment outside block.")
195 v
.control_flow_ctx
.unreash
= true
198 redef class AContinueExpr
199 special ABlockControler
200 redef meth accept_control_flow
(v
)
203 var block
= v
.control_flow_ctx
.base_block
204 if block
== null then
205 v
.error
(self, "Syntax Error: 'continue' outside block.")
209 v
.control_flow_ctx
.unreash
= true
213 redef class AAbortExpr
214 redef meth accept_control_flow
(v
)
217 v
.control_flow_ctx
.has_return
= true
218 v
.control_flow_ctx
.unreash
= true
223 redef meth accept_control_flow
(v
)
227 var old_control_flow_ctx
= v
.control_flow_ctx
228 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
232 if n_else
== null then
233 # Restore control flow ctx since the 'then" block is optional
234 v
.control_flow_ctx
= old_control_flow_ctx
236 # Remember what appens in the 'then'
237 var then_control_flow_ctx
= v
.control_flow_ctx
238 # Reset to execute the 'else'
239 v
.control_flow_ctx
= old_control_flow_ctx
.sub
243 # Merge then and else in the old control_flow
244 old_control_flow_ctx
.has_return
= v
.control_flow_ctx
.has_return
and then_control_flow_ctx
.has_return
245 old_control_flow_ctx
.unreash
= v
.control_flow_ctx
.unreash
and then_control_flow_ctx
.unreash
247 if v
.control_flow_ctx
.unreash
then v
.control_flow_ctx
= then_control_flow_ctx
248 if then_control_flow_ctx
.unreash
then then_control_flow_ctx
= v
.control_flow_ctx
249 for variable
in v
.control_flow_ctx
.set_variables
do
250 if then_control_flow_ctx
.is_set
(variable
) then
251 old_control_flow_ctx
.set_variables
.add
(variable
)
254 v
.control_flow_ctx
= old_control_flow_ctx
259 class AControlableBlock
261 redef meth accept_control_flow
(v
)
263 # Store old control flow values
264 var old_control_flow_ctx
= v
.control_flow_ctx
265 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
268 v
.control_flow_ctx
.base_block
= self
272 # Restore control flow value since all controlable blocks are optionnal
273 v
.control_flow_ctx
= old_control_flow_ctx
277 redef class AWhileExpr
278 special AControlableBlock
282 special AControlableBlock
285 redef class AForVardeclExpr
286 redef meth accept_control_flow
(v
)
289 v
.mark_is_set
(variable
)
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
)
320 redef class AOnceExpr
321 redef meth accept_control_flow
(v
)
323 if v
.once_count
> 0 then
324 v
.warning
(self, "Useless once in a once expression.")
326 v
.once_count
= v
.once_count
+ 1
330 v
.once_count
= v
.once_count
- 1