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 Env
= Typing_env
14 module SN
= Naming_special_names
16 module FuncTerm
= Typing_func_terminality
18 let static_meth_terminal env ci meth_id
=
19 let class_name = match ci
with
20 | CI
(cls_id
, _
) -> Some
(snd cls_id
)
21 | CIself
| CIstatic
-> Some
(Typing_env.get_self_id env
)
22 | CIparent
-> Some
(Typing_env.get_parent_id env
)
23 | CIexpr _
-> None
(* we declared the types, but didn't check the bodies yet
24 so can't tell anything here *)
28 FuncTerm.raise_exit_if_terminal
29 (FuncTerm.get_static_meth
(Env.get_options env
) class_name (snd meth_id
))
32 (* Module coded with an exception, if we find a terminal statement we
33 * throw the exception Exit.
36 val case
: Typing_env.env
-> case
-> bool
37 val block
: Typing_env.env
-> block
-> bool
41 let rec terminal env inside_case stl
=
42 List.iter stl
(terminal_ env inside_case
)
44 and terminal_ env inside_case
= function
45 | Break _
-> if inside_case
then () else raise Exit
50 | Expr
(_
, Yield_break
)
51 | Expr
(_
, Assert
(AE_assert
(_
, False
)))
53 | Expr
(_
, Call
(Cnormal
, (_
, Id
(_
, fun_name
)), _
, _
, _
)) ->
54 let tcopt = Env.get_options env
in
55 FuncTerm.raise_exit_if_terminal
(FuncTerm.get_fun
tcopt fun_name
)
56 | Expr
(_
, Call
(Cnormal
, (_
, Class_const
(((), ci
), meth_id
)), _
, _
, _
)) ->
57 static_meth_terminal env ci meth_id
58 | If
((_
, True
), b1
, _
) -> terminal env inside_case b1
59 | If
((_
, False
), _
, b2
) -> terminal env inside_case b2
61 (try terminal env inside_case b1
; () with Exit
->
62 terminal env inside_case b2
)
65 | Try
(b
, catch_list
, _
) ->
66 (* Note: return inside a finally block is allowed in PHP and
67 * overrides any return in try or catch. It is an error in <?hh,
68 * however. The only way that a finally block can thus be
69 * terminal is if it throws unconditionally -- however, there's
70 * no good case I (eletuchy) could think of for why one would
71 * write *always* throwing code inside a finally block.
73 (try terminal env inside_case b
; () with Exit
->
74 terminal_catchl env inside_case catch_list
)
76 terminal env inside_case b
77 | While
((_
, True
), b
)
79 | For
((_
, Expr_list
[]), (_
, Expr_list
[]), (_
, Expr_list
[]), b
) ->
80 if not
(Nast.Visitor.HasBreak.block b
) then raise Exit
94 and terminal_catchl env inside_case
= function
98 terminal env inside_case x
100 terminal_catchl env inside_case rl
103 and terminal_cl env
= function
104 (* Empty list case should only be when switch statement is malformed and has
105 no case or default blocks *)
107 | [Case
(_
, b
)] | [Default b
] -> terminal env
true b
108 | Case
(_
, b
) :: rl
->
111 (* TODO check this *)
112 if List.exists b
(function Break _
-> true | _
-> false)
115 with Exit
-> terminal_cl env rl
)
117 (try terminal env
true b
with Exit
->
120 and terminal_case env
= function
121 | Case
(_
, b
) | Default b
-> terminal env
true b
124 try terminal env
false stl
; false with Exit
-> true
127 try terminal_case env c
; false with Exit
-> true
131 (* TODO jwatzman #3076304 convert this and Terminal to visitor pattern to
132 * remove copy-pasta *)
134 val check
: Pos.t
-> Typing_env.env
-> case list
-> unit
137 let rec terminal env stl
=
138 List.iter stl
(terminal_ env
)
140 and terminal_ env
= function
147 | Expr
(_
, Yield_break
)
148 | Expr
(_
, Assert
(AE_assert
(_
, False
))) -> raise Exit
149 | Expr
(_
, Call
(Cnormal
, (_
, Id
(_
, fun_name
)), _
, _
, _
)) ->
150 let tcopt = Env.get_options env
in
151 FuncTerm.raise_exit_if_terminal
(FuncTerm.get_fun
tcopt fun_name
)
152 | Expr
(_
, Call
(Cnormal
, (_
, Class_const
(((), ci
), meth_id
)), _
, _
, _
)) ->
153 static_meth_terminal env ci meth_id
154 | If
((_
, True
), b1
, _
) -> terminal env b1
155 | If
((_
, False
), _
, b2
) -> terminal env b2
157 (try terminal env b1
; () with Exit
-> terminal env b2
)
160 | Try
(b
, catches
, _
) ->
161 (* NOTE: contents of finally block are not executed in normal flow, so
162 * they cannot contribute to terminality *)
163 (try terminal env b
; ()
164 with Exit
-> terminal_catchl env catches
)
178 and terminal_catchl env
= function
184 terminal_catchl env rl
187 and terminal_cl env
= function
188 (* Empty list case should only be when switch statement is malformed and has
189 no case or default blocks *)
191 | [Case
(_
, b
)] | [Default b
] -> terminal env b
192 | Case
(_
, b
) :: rl
->
195 (* TODO check this *)
196 if List.exists b
(function Break _
-> true | _
-> false)
199 with Exit
-> terminal_cl env rl
)
201 (try terminal env b
with Exit
-> terminal_cl env rl
)
203 let check p env
= function
204 | [] -> () (* Skip empty cases so we can use tl below *)
205 | cl
-> (* Skip the last case *)
206 List.iter
(List.tl_exn
(List.rev cl
)) begin fun c
->
208 (* Allow empty cases to fall through *)
211 | Case
(e
, b
) -> begin
213 Errors.case_fallthrough p
(fst e
)
217 Errors.default_fallthrough p