First NIT release and new clean mercurial repository
[nit.git] / src / syntax / control_flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Analysis control flow in property bodies, statements and expressions
18 package control_flow
19
20 import syntax_base
21
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)
26 do
27 var tv = new ControlFlowVisitor(tc, self)
28 tv.visit(node)
29 end
30 end
31
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
38 redef meth visit(n)
39 do
40 if n != null then n.accept_control_flow(self)
41 end
42
43 # Number of nested once
44 readable writable attr _once_count: Int
45
46 # Current knowledge about variables types
47 readable writable attr _control_flow_ctx: ControlFlowContext
48
49 init(tc, m) do super
50 end
51
52 private class ControlFlowContext
53 # Previous control flow context if any
54 readable attr _prev: ControlFlowContext
55
56 # Is a return met?
57 readable writable attr _has_return: Bool
58
59 # Is a control flow break met? (return, break, continue)
60 readable writable attr _unreash: Bool
61
62 # Is a control flow already broken?
63 # Used to avoid repeating the same error message
64 readable writable attr _already_unreash: Bool
65
66 # Current controlable block (for or while)
67 readable writable attr _base_block: AControlableBlock
68
69 meth sub: ControlFlowContext
70 do
71 return new ControlFlowContext.with(self)
72 end
73
74 init
75 do
76 end
77
78 init with(p: ControlFlowContext)
79 do
80 _prev = p
81 _has_return = p.has_return
82 _unreash = p.unreash
83 _already_unreash = p.already_unreash
84 _base_block = p.base_block
85 end
86 end
87
88 ###############################################################################
89
90 redef class PNode
91 private meth accept_control_flow(v: ControlFlowVisitor)
92 do
93 accept_abs_syntax_visitor(v)
94 end
95 end
96
97 redef class AMethPropdef
98 redef meth accept_control_flow(v)
99 do
100 v.control_flow_ctx = new ControlFlowContext
101 super
102 end
103 end
104
105 redef class AConcreteMethPropdef
106 redef meth accept_control_flow(v)
107 do
108 super
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.")
111 end
112 end
113 end
114
115 redef class ABlockExpr
116 redef meth accept_control_flow(v)
117 do
118 for e in n_expr do
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.")
122 end
123 v.visit(e)
124 end
125 end
126 end
127
128 redef class AReturnExpr
129 redef meth accept_control_flow(v)
130 do
131 super
132 v.control_flow_ctx.has_return = true
133 v.control_flow_ctx.unreash = true
134 end
135 end
136
137 class ABlockControler
138 special PExpr
139 readable attr _block: AControlableBlock
140 end
141
142 redef class ABreakExpr
143 special ABlockControler
144 redef meth accept_control_flow(v)
145 do
146 super
147 var block = v.control_flow_ctx.base_block
148 if block == null then
149 v.error(self, "Syntax Error: 'break' statment outside block.")
150 return
151 end
152 _block = block
153 v.control_flow_ctx.unreash = true
154 end
155 end
156 redef class AContinueExpr
157 special ABlockControler
158 redef meth accept_control_flow(v)
159 do
160 super
161 var block = v.control_flow_ctx.base_block
162 if block == null then
163 v.error(self, "Syntax Error: 'continue' outside block.")
164 return
165 end
166 _block = block
167 v.control_flow_ctx.unreash = true
168 end
169 end
170
171 redef class AAbortExpr
172 redef meth accept_control_flow(v)
173 do
174 super
175 v.control_flow_ctx.has_return = true
176 v.control_flow_ctx.unreash = true
177 end
178 end
179
180 redef class AIfExpr
181 redef meth accept_control_flow(v)
182 do
183 v.visit(n_expr)
184
185 var old_control_flow_ctx = v.control_flow_ctx
186 v.control_flow_ctx = v.control_flow_ctx.sub
187
188 v.visit(n_then)
189
190 if n_else == null then
191 # Restore control flow ctx
192 v.control_flow_ctx = old_control_flow_ctx
193 else
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
198
199 v.visit(n_else)
200
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
205 end
206 end
207 end
208
209 class AControlableBlock
210 special PExpr
211 redef meth accept_control_flow(v)
212 do
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
216
217 # Register the block
218 v.control_flow_ctx.base_block = self
219
220 super
221
222 # Restore control flow value
223 v.control_flow_ctx = old_control_flow_ctx
224 end
225 end
226
227 redef class AWhileExpr
228 special AControlableBlock
229 end
230
231 redef class AForExpr
232 special AControlableBlock
233 end
234
235 redef class AOnceExpr
236 redef meth accept_control_flow(v)
237 do
238 if v.once_count > 0 then
239 v.warning(self, "Useless once in a once expression.")
240 end
241 v.once_count = v.once_count + 1
242
243 super
244
245 v.once_count = v.once_count - 1
246 end
247 end
248