2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
13 module FuncTerm
= Typing_func_terminality
14 module NS
= Namespaces
16 (* Module calculating the locals for a statement
17 * This is useful when someone uses $x on both sides
18 * of an If statement, for example:
27 ((nsenv
: Namespace_env.env
), (m1
: Pos.t
SMap.t
)) (m2
: Pos.t
SMap.t
) =
28 let m_combined = SMap.fold
SMap.add m1 m2
in
31 let rec lvalue ((nsenv
, m
) as acc
) (p
, e
) =
33 | Aast.List lv
-> List.fold_left ~init
:acc ~f
:lvalue lv
34 | Aast.Lvar
(_
, lid
) -> (nsenv
, SMap.add
(Local_id.to_string lid
) p m
)
35 | Aast.Unop
(Uref
, (p
, Aast.Lvar
(_
, lid
))) ->
36 (nsenv
, SMap.add
(Local_id.to_string lid
) p m
)
39 (* TODO It really sucks that this and Nast_terminality.Terminal are very
40 * slightly different (notably, this version is somewhat buggier). Fixing that
41 * exposes a lot of errors in www unfortunately -- we should bite the bullet on
42 * fixing switch all the way when we do that, most likely though -- see tasks
43 * #3140431 and #2813555. *)
44 let rec terminal nsenv ~in_try stl
= List.iter stl
(terminal_ nsenv ~in_try
)
46 and terminal_ nsenv ~in_try st
=
48 | Aast.Throw _
when not in_try
-> raise Exit
54 ( Aast.Call
(_
, (_
, Aast.Id
(_
, "assert")), _
, [(_
, Aast.False
)], [])
56 (_
, (_
, Aast.Id
(_
, "invariant")), _
, (_
, Aast.False
) :: _
:: _
, [])
60 | Aast.Expr
(_
, Aast.Call
(_
, (_
, Aast.Id fun_id
), _
, _
, _
)) ->
61 let (_
, fun_name
) = NS.elaborate_id nsenv
NS.ElaborateFun fun_id
in
62 FuncTerm.(raise_exit_if_terminal
(get_fun fun_name
))
69 ((_
, Aast.CIexpr
(_
, Aast.Id cls_id
)), (_
, meth_name
)) ),
73 let (_
, cls_name
) = NS.elaborate_id nsenv
NS.ElaborateClass cls_id
in
74 FuncTerm.(raise_exit_if_terminal
(get_static_meth cls_name meth_name
))
75 | Aast.If
(_
, b1
, b2
) ->
77 terminal nsenv ~in_try b1
;
79 with Exit
-> terminal nsenv ~in_try b2
)
80 | Aast.Switch
(_
, cl
) -> terminal_cl nsenv ~in_try cl
81 | Aast.Block b
-> terminal nsenv ~in_try b
82 | Aast.Using u
-> terminal nsenv ~in_try u
.Aast.us_block
83 | Aast.Try
(b
, catch_l
, _fb
) ->
84 (* return is not allowed in finally, so we can ignore fb *)
85 terminal nsenv ~in_try
:true b
;
86 List.iter catch_l
(terminal_catch nsenv ~in_try
)
88 (* TODO this is terminal sometimes too, except switch, see above. *)
106 and terminal_catch nsenv ~in_try
(_
, _
, b
) = terminal nsenv ~in_try b
108 and terminal_cl nsenv ~in_try
= function
110 | Aast.Case
(_
, b
) :: rl
->
112 terminal nsenv ~in_try b
;
113 if blockHasBreak b
then
117 with Exit
-> terminal_cl nsenv ~in_try rl
)
118 | Aast.Default
(_
, b
) :: rl
->
120 try terminal nsenv ~in_try b
with Exit
-> terminal_cl nsenv ~in_try rl
123 and blockHasBreak
= function
125 | (_
, Aast.Break
) :: _
-> true
129 | Aast.If
(_
, [], []) -> false
131 | Aast.If
(_
, [], b
) ->
133 | Aast.If
(_
, b1
, b2
) -> blockHasBreak b1
&& blockHasBreak b2
136 x'
|| blockHasBreak xs
138 let is_terminal nsenv stl
=
140 terminal nsenv ~in_try
:false stl
;
144 let rec expr acc
(_
, e
) =
145 let expr_expr acc e1 e2
=
146 let acc = expr acc e1
in
147 let acc = expr acc e2
in
152 | Aast.AFvalue e
-> expr acc e
153 | Aast.AFkvalue
(k
, v
) -> expr_expr acc k v
155 let exprs acc es
= List.fold_left es ~init
:acc ~f
:expr in
157 | Aast.Binop
(Eq None
, lv
, rv
) ->
158 let acc = expr acc rv
in
161 | Aast.Collection
(_
, _
, fields
) ->
162 List.fold_left fields ~init
:acc ~f
:field
163 | Aast.Varray
(_
, es
)
168 | Aast.PrefixedString
(_
, e
) -> expr acc e
169 | Aast.Darray
(_
, exprexprs
) ->
170 List.fold_left exprexprs ~init
:acc ~f
:(fun acc (e1
, e2
) ->
172 | Aast.Shape fields
->
173 List.fold_left fields ~init
:acc ~f
:(fun acc (_
, e
) -> expr acc e
)
179 | Aast.ParenthesizedExpr e
182 | Aast.Class_const
((_
, Aast.CIexpr e
), _
)
183 | Aast.Callconv
(_
, e
)
188 | Aast.Obj_get
(e1
, e2
, _
)
189 | Aast.Binop
(_
, e1
, e2
)
190 | Aast.Pipe
(_
, e1
, e2
)
191 | Aast.Class_get
((_
, Aast.CIexpr e1
), Aast.CGexpr e2
) ->
193 | Aast.Class_get
((_
, Aast.CIexpr e1
), _
) -> expr acc e1
195 | Aast.Class_get _
->
196 failwith
"Unexpected Expr: Typing_get_locals expected CIexpr"
197 | Aast.Array_get
(e1
, oe2
) ->
198 let acc = expr acc e1
in
199 let acc = Option.value_map oe2 ~default
:acc ~f
:(expr acc) in
201 | Aast.New
((_
, Aast.CIexpr e1
), _
, es2
, es3
, _
)
202 | Aast.Call
(_
, e1
, _
, es2
, es3
) ->
203 let acc = expr acc e1
in
204 let acc = exprs acc es2
in
205 let acc = exprs acc es3
in
208 failwith
"Unexpected Expr: Typing_get_locals expected CIexpr in New"
209 | Aast.Record
((_
, Aast.CIexpr e1
), _
, exprexprs
) ->
210 let acc = expr acc e1
in
211 List.fold_left exprexprs ~init
:acc ~f
:(fun acc (e1
, e2
) ->
214 failwith
"Unexpected Expr: Typing_get_locals expected CIexpr in Record"
215 | Aast.Yield f
-> field acc f
216 | Aast.Eif
(e1
, oe2
, e3
) ->
217 let acc = expr acc e1
in
218 let (_
, acc2
) = Option.value_map oe2 ~default
:acc ~f
:(expr acc) in
219 let (_
, acc3
) = expr acc e3
in
220 smap_union acc (smap_inter acc2 acc3
)
221 | Aast.Xml
(_
, attribs
, es
) ->
224 | Aast.Xhp_simple
(_
, e
)
225 | Aast.Xhp_spread e
->
228 let acc = List.fold_left attribs ~init
:acc ~f
:attrib in
229 let acc = exprs acc es
in
244 | Aast.PU_identifier _
->
246 (* These are not in the original AST *)
249 | Aast.ValCollection _
250 | Aast.KeyValCollection _
251 | Aast.ImmutableVar _
252 | Aast.Dollardollar _
253 | Aast.Lplaceholder _
256 | Aast.Method_caller _
258 | Aast.Special_func _
262 failwith
"Unexpected Expr: Typing_get_locals expr not found on legacy AST"
264 let rec stmt (acc : Namespace_env.env
* Pos.t
SMap.t
) st
=
265 let nsenv = fst
acc in
267 | Aast.Expr e
-> expr acc e
273 | Aast.TempContinue _
277 let acc = block
acc b
in
278 let acc = expr acc e
in
280 | Aast.While
(e
, _b
) -> expr acc e
281 | Aast.For
(e1
, e2
, _e3
, _b
) ->
282 let acc = expr acc e1
in
283 let acc = expr acc e2
in
285 | Aast.Foreach
(e
, as_e
, _b
) ->
286 let acc = expr acc e
in
290 | Aast.Await_as_v
(_
, v
) ->
293 | Aast.Await_as_kv
(_
, k
, v
) ->
294 let acc = expr acc k
in
295 let acc = expr acc v
in
304 | Aast.Awaitall
(el
, b
) ->
306 List.fold_left ~init
:acc ~f
:(fun acc (_
, e2
) -> expr acc e2
) el
308 let acc = block
acc b
in
310 | Aast.Let
(_x
, _h
, e
) ->
311 (* We would like to exclude scoped locals here, but gather the locals in
314 | Aast.Using u
-> block
acc u
.Aast.us_block
315 | Aast.Block b
-> block
acc b
316 | Aast.If
(e
, b1
, b2
) ->
317 let acc = expr acc e
in
318 let term1 = is_terminal nsenv b1
in
319 let term2 = is_terminal nsenv b2
in
320 if term1 && term2 then
323 let (_
, m2
) = block
(nsenv, SMap.empty
) b2
in
326 let (_
, m1
) = block
(nsenv, SMap.empty
) b1
in
329 let (_
, m1
) = block
(nsenv, SMap.empty
) b1
in
330 let (_
, m2
) = block
(nsenv, SMap.empty
) b2
in
331 let (m
: Pos.t
SMap.t
) = smap_inter m1 m2
in
333 | Aast.Switch
(e
, cl
) ->
334 let acc = expr acc e
in
336 List.filter
cl ~f
:(function
338 | Aast.Default
(_
, b
)
339 -> not
(is_terminal nsenv b
))
341 let cl = casel
nsenv cl in
342 let c = smap_inter_list
cl in
344 | Aast.Try
(b
, cl, _fb
) ->
345 let (_
, c) = block
(nsenv, SMap.empty
) b
in
346 let cl = List.filter
cl ~f
:(fun (_
, _
, b
) -> not
(is_terminal nsenv b
)) in
347 let lcl = List.map
cl (catch
nsenv) in
348 let c = smap_inter_list
(c :: lcl) in
351 and block
acc l
= List.fold_left ~init
:acc ~f
:(fun acc st
-> stmt acc st
) l
356 | Aast.Case
(_
, []) :: rl
-> casel
nsenv rl
357 | Aast.Default
(_
, b
) :: rl
358 | Aast.Case
(_
, b
) :: rl
->
359 let (_
, b
) = block
(nsenv, SMap.empty
) b
in
362 and catch
nsenv (_
, _
, b
) = snd
(block
(nsenv, SMap.empty
) b
)