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
52 private class ControlFlowContext
53 # Previous control flow context if any
54 readable attr _prev
: ControlFlowContext
57 readable writable attr _has_return
: Bool
59 # Is a control flow break met? (return, break, continue)
60 readable writable attr _unreash
: Bool
62 # Is a control flow already broken?
63 # Used to avoid repeating the same error message
64 readable writable attr _already_unreash
: Bool
66 # Current controlable block (for or while)
67 readable writable attr _base_block
: AControlableBlock
69 meth sub
: ControlFlowContext
71 return new ControlFlowContext.with
(self)
78 init with
(p
: ControlFlowContext)
81 _has_return
= p
.has_return
83 _already_unreash
= p
.already_unreash
84 _base_block
= p
.base_block
88 ###############################################################################
91 private meth accept_control_flow
(v
: ControlFlowVisitor)
93 accept_abs_syntax_visitor
(v
)
97 redef class AMethPropdef
98 redef meth accept_control_flow
(v
)
100 v
.control_flow_ctx
= new ControlFlowContext
105 redef class AConcreteMethPropdef
106 redef meth accept_control_flow
(v
)
109 if v
.control_flow_ctx
.has_return
== false and method
.signature
.return_type
!= null then
110 v
.error
(self, "Control error: Reached end of function.")
115 redef class ABlockExpr
116 redef meth accept_control_flow
(v
)
119 if v
.control_flow_ctx
.unreash
and not v
.control_flow_ctx
.already_unreash
then
120 v
.control_flow_ctx
.already_unreash
= true
121 v
.warning
(e
, "Warning: unreachable statement.")
128 redef class AReturnExpr
129 redef meth accept_control_flow
(v
)
132 v
.control_flow_ctx
.has_return
= true
133 v
.control_flow_ctx
.unreash
= true
137 class ABlockControler
139 readable attr _block
: AControlableBlock
142 redef class ABreakExpr
143 special ABlockControler
144 redef meth accept_control_flow
(v
)
147 var block
= v
.control_flow_ctx
.base_block
148 if block
== null then
149 v
.error
(self, "Syntax Error: 'break' statment outside block.")
153 v
.control_flow_ctx
.unreash
= true
156 redef class AContinueExpr
157 special ABlockControler
158 redef meth accept_control_flow
(v
)
161 var block
= v
.control_flow_ctx
.base_block
162 if block
== null then
163 v
.error
(self, "Syntax Error: 'continue' outside block.")
167 v
.control_flow_ctx
.unreash
= true
171 redef class AAbortExpr
172 redef meth accept_control_flow
(v
)
175 v
.control_flow_ctx
.has_return
= true
176 v
.control_flow_ctx
.unreash
= true
181 redef meth accept_control_flow
(v
)
185 var old_control_flow_ctx
= v
.control_flow_ctx
186 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
190 if n_else
== null then
191 # Restore control flow ctx
192 v
.control_flow_ctx
= old_control_flow_ctx
194 # Remember what appens in the 'then'
195 var then_control_flow_ctx
= v
.control_flow_ctx
196 # Reset to execute the 'else'
197 v
.control_flow_ctx
= old_control_flow_ctx
201 # Restore and conclude
202 v
.control_flow_ctx
= old_control_flow_ctx
203 v
.control_flow_ctx
.has_return
= v
.control_flow_ctx
.has_return
and then_control_flow_ctx
.has_return
204 v
.control_flow_ctx
.unreash
= v
.control_flow_ctx
.unreash
and then_control_flow_ctx
.unreash
209 class AControlableBlock
211 redef meth accept_control_flow
(v
)
213 # Store old control flow values
214 var old_control_flow_ctx
= v
.control_flow_ctx
215 v
.control_flow_ctx
= v
.control_flow_ctx
.sub
218 v
.control_flow_ctx
.base_block
= self
222 # Restore control flow value
223 v
.control_flow_ctx
= old_control_flow_ctx
227 redef class AWhileExpr
228 special AControlableBlock
232 special AControlableBlock
235 redef class AOnceExpr
236 redef meth accept_control_flow
(v
)
238 if v
.once_count
> 0 then
239 v
.warning
(self, "Useless once in a once expression.")
241 v
.once_count
= v
.once_count
+ 1
245 v
.once_count
= v
.once_count
- 1