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.
14 module FuncTerm
= Typing_func_terminality
15 module NS
= Namespaces
17 (* Module calculating the locals for a statement
18 * This is useful when someone uses $x on both sides
19 * of an If statement, for example:
27 (* TODO It really sucks that this and Nast_terminality.Terminal are very
28 * slightly different (notably, this version is somewhat buggier). Fixing that
29 * exposes a lot of errors in www unfortunately -- we should bite the bullet on
30 * fixing switch all the way when we do that, most likely though -- see tasks
31 * #3140431 and #2813555. *)
32 let rec terminal tcopt nsenv ~in_try stl
=
33 List.iter stl
(terminal_ tcopt nsenv ~in_try
)
35 and terminal_ tcopt nsenv ~in_try
(_
, st_
) =
37 | Throw _
when not in_try
-> raise Exit
40 | Expr
(_
, ( Call
((_
, Id
(_
, "assert")), _
, [_
, False
], [])
41 | Call
((_
, Id
(_
, "invariant")), _
, (_
, False
) :: _
:: _
, [])))
42 | Return _
-> raise Exit
43 | Expr
(_
, Call
((_
, Id fun_id
), _
, _
, _
)) ->
44 let _, fun_name
= NS.elaborate_id nsenv
NS.ElaborateFun fun_id
in
45 FuncTerm.(raise_exit_if_terminal
(get_fun tcopt fun_name
))
46 | Expr
(_, Call
((_, Class_const
((_, Id cls_id
), (_, meth_name
))), _, _, _)) ->
47 let _, cls_name
= NS.elaborate_id nsenv
NS.ElaborateClass cls_id
in
48 FuncTerm.(raise_exit_if_terminal
49 (get_static_meth tcopt cls_name meth_name
))
51 (try terminal tcopt nsenv ~in_try b1
; () with Exit
->
52 terminal tcopt nsenv ~in_try b2
)
54 terminal_cl tcopt nsenv ~in_try cl
55 | Block b
-> terminal tcopt nsenv ~in_try b
56 | Using u
-> terminal tcopt nsenv ~in_try u
.us_block
57 | Try
(b
, catch_l
, _fb
) ->
58 (* return is not allowed in finally, so we can ignore fb *)
59 (terminal tcopt nsenv ~in_try
:true b
;
60 List.iter catch_l
(terminal_catch tcopt nsenv ~in_try
))
73 | Break
_ (* TODO this is terminal sometimes too, except switch, see above. *)
81 and terminal_catch tcopt nsenv ~in_try
(_, _, b
) =
82 terminal tcopt nsenv ~in_try b
84 and terminal_cl tcopt nsenv ~in_try
= function
86 | Case
(_, b
) :: rl
->
88 terminal tcopt nsenv ~in_try b
;
92 with Exit
-> terminal_cl tcopt nsenv ~in_try rl
)
94 begin try terminal tcopt nsenv ~in_try b
with
96 terminal_cl tcopt nsenv ~in_try rl
99 and blockHasBreak
= function
101 | (_, Break
_) :: _ -> true
105 | _, If
(_, [], []) -> false
106 | _, If
(_, b
, []) | _, If
(_, [], b
) -> blockHasBreak b
107 | _, If
(_, b1
, b2
) -> blockHasBreak b1
&& blockHasBreak b2
110 x'
|| blockHasBreak xs
112 let is_terminal tcopt nsenv stl
=
113 try terminal tcopt nsenv ~in_try
:false stl
; false
116 let smap_union ((nsenv
:Namespace_env.env
), (m1
:Pos.t
SMap.t
))
118 let m_combined = SMap.fold
SMap.add m1 m2
in
121 let rec lvalue tcopt
(acc
:(Namespace_env.env
* Pos.t
SMap.t
)) = function
122 | (p
, Lvar
(_, x)) ->
123 let nsenv, m
= acc
in
124 nsenv, SMap.add
x p m
125 | _, List lv
-> List.fold_left lv ~init
:acc ~f
:(lvalue tcopt
)
126 (* Ref forms a local inside a foreach *)
127 | (_, Unop
(Uref
, (p
, Lvar
(_, x)))) ->
128 let nsenv, m
= acc
in
129 nsenv, SMap.add
x p m
132 let rec stmt tcopt
(acc
:(Namespace_env.env
* Pos.t
SMap.t
)) (_, st_
) =
133 let nsenv = fst acc
in
135 | Expr e
-> expr tcopt acc e
139 | Break
_ | Continue
_ | Throw
_ -> acc
141 let acc = block tcopt
acc b
in
142 let acc = expr tcopt
acc e
in
144 | While
(e
, _b
) -> expr tcopt
acc e
145 | For
(e1
, e2
, _e3
, _b
) ->
146 let acc = expr tcopt
acc e1
in
147 let acc = expr tcopt
acc e2
in
149 | Foreach
(e
, _await
, as_e
, _b
) ->
150 let acc = expr tcopt
acc e
in
151 begin match as_e
with
152 | As_v v
-> expr tcopt
acc v
154 let acc = expr tcopt
acc k
in
155 let acc = expr tcopt
acc v
in
159 | Return
_ | GotoLabel
_ | Goto
_ | Static_var
_
160 | Global_var
_ | Def_inline
_ | Noop
-> acc
162 List.fold_left el ~init
:acc ~f
:(fun acc (_, e2
) ->
166 (* We would like to exclude scoped locals here, but gather the locals in
169 | Using u
-> block tcopt
acc u
.us_block
170 | Block b
-> block tcopt
acc b
172 let acc = expr tcopt
acc e
in
173 let term1 = is_terminal tcopt
nsenv b1
in
174 let term2 = is_terminal tcopt
nsenv b2
in
179 let _, m2
= block tcopt
(nsenv, SMap.empty
) b2
in
183 let _, m1
= block tcopt
(nsenv, SMap.empty
) b1
in
186 let _, m1
= block tcopt
(nsenv, SMap.empty
) b1
in
187 let _, m2
= block tcopt
(nsenv, SMap.empty
) b2
in
188 let (m
:Pos.t
SMap.t
) = (smap_inter m1 m2
) in
192 let acc = expr tcopt
acc e
in
193 let cl = List.filter
cl begin function
195 | Default b
-> not
(is_terminal tcopt
nsenv b
)
197 let cl = casel tcopt
nsenv cl in
198 let c = smap_inter_list
cl in
200 | Try
(b
, cl, _fb
) ->
201 let _, c = block tcopt
(nsenv, SMap.empty
) b
in
202 let cl = List.filter
cl begin fun (_, _, b
) ->
203 not
(is_terminal tcopt
nsenv b
)
205 let lcl = List.map
cl (catch tcopt
nsenv) in
206 let c = smap_inter_list
(c :: lcl) in
209 and block tcopt
acc l
= List.fold_left l ~init
:acc ~f
:(stmt tcopt
)
211 and casel tcopt
nsenv = function
213 | Case
(_, []) :: rl
-> casel tcopt
nsenv rl
215 | Case
(_, b
) :: rl
->
216 let _, b
= block tcopt
(nsenv, SMap.empty
) b
in
217 b
:: casel tcopt
nsenv rl
219 and catch tcopt
nsenv (_, _, b
) =
220 snd
(block tcopt
(nsenv, SMap.empty
) b
)
222 and expr tcopt
acc (_, e
) =
223 let expr_expr acc e1 e2
=
224 let acc = expr tcopt
acc e1
in
225 let acc = expr tcopt
acc e2
in
230 | AFvalue e
-> expr tcopt
acc e
231 | AFkvalue
(k
, v
) -> expr_expr acc k v
234 List.fold_left es ~init
:acc ~f
:(expr tcopt
)
237 | Binop
(Eq None
, lv
, rv
) ->
238 let acc = expr tcopt
acc rv
in
241 | Collection
(_, fields
) ->
242 List.fold_left fields ~init
:acc ~f
:field
246 | Execution_operator es
249 | PrefixedString
(_, e
) -> expr tcopt
acc e
250 | Darray exprexprs
->
251 List.fold_left exprexprs ~init
:acc ~f
:(fun acc -> fun (e1
, e2
) -> expr_expr acc e1 e2
)
253 List.fold_left fields ~init
:acc ~f
:(fun acc -> fun (_, e
) -> expr tcopt
acc e
)
259 | ParenthesizedExpr e
266 | Suspend e
-> expr tcopt
acc e
267 | Obj_get
(e1
, e2
, _)
270 | InstanceOf
(e1
, e2
)
271 | Class_get
(e1
, e2
) -> expr_expr acc e1 e2
272 | Array_get
(e1
, oe2
) ->
273 let acc = expr tcopt
acc e1
in
274 let acc = Option.value_map oe2 ~default
:acc ~f
:(expr tcopt
acc) in
276 | New
(e1
, _, es2
, es3
)
277 | Call
(e1
, _, es2
, es3
) ->
278 let acc = expr tcopt
acc e1
in
279 let acc = exprs acc es2
in
280 let acc = exprs acc es3
in
284 | Eif
(e1
, oe2
, e3
) ->
285 let acc = expr tcopt
acc e1
in
286 let _, acc2
= Option.value_map oe2 ~default
:acc ~f
:(expr tcopt
acc) in
287 let _, acc3
= expr tcopt
acc e3
in
288 smap_union acc (smap_inter acc2 acc3
)
289 | NewAnonClass
(es1
, es2
, _) ->
290 let acc = exprs acc es1
in
291 let acc = exprs acc es2
in
293 | Xml
(_, attribs
, es
) ->
297 | Xhp_spread e
-> expr tcopt
acc e
299 let acc = List.fold_left attribs ~init
:acc ~f
:attrib in
300 let acc = exprs acc es
in
314 | Unsafeexpr
_ -> acc