sync the repo
[hiphop-php.git] / hphp / hack / src / typing / tast_check / rvalue_check.ml
blobdc2a46dfb22395bdbeba18dc44b02bdfc49f42c9
1 (*
2 * Copyright (c) 2018, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Hh_prelude
11 open Aast
12 open Typing_defs
13 module Env = Tast_env
14 module Reason = Typing_reason
16 let check_valid_rvalue pos env ty =
17 let rec iter_over_types env tyl =
18 match tyl with
19 | [] -> env
20 | ty :: tyl ->
21 let (env, ety) = Env.expand_type env ty in
22 (match deref ety with
23 | (r, Tprim Tnoreturn) ->
24 Typing_error_utils.add_typing_error
25 ~env:(Tast_env.tast_env_as_typing_env env)
26 Typing_error.(
27 wellformedness
28 @@ Primary.Wellformedness.Noreturn_usage
30 pos;
31 reason =
32 lazy
33 (Reason.to_string
34 "A `noreturn` function always throws or exits."
35 r);
36 });
37 env
38 | (r, Tprim Tvoid) ->
39 Typing_error_utils.add_typing_error
40 ~env:(Tast_env.tast_env_as_typing_env env)
41 Typing_error.(
42 wellformedness
43 @@ Primary.Wellformedness.Void_usage
45 pos;
46 reason =
47 lazy
48 (Reason.to_string
49 "A `void` function doesn't return a value."
50 r);
51 });
52 env
53 | (_, Tunion tyl2) -> iter_over_types env (tyl2 @ tyl)
54 | (_, _) -> iter_over_types env tyl)
56 ignore (iter_over_types env [ty])
58 let visitor =
59 object (this)
60 inherit [_] Aast.iter as super
62 val non_returning_allowed = ref true
64 method allow_non_returning f =
65 let is_non_returning_allowed = !non_returning_allowed in
66 non_returning_allowed := true;
67 f ();
68 non_returning_allowed := is_non_returning_allowed
70 method disallow_non_returning f =
71 let is_non_returning_allowed = !non_returning_allowed in
72 non_returning_allowed := false;
73 f ();
74 non_returning_allowed := is_non_returning_allowed
76 method! on_expr env ((ty, p, e) as te) =
77 match e with
78 | Binop { bop = Ast_defs.Eq None; lhs; rhs } ->
79 this#allow_non_returning (fun () -> this#on_expr env lhs);
80 this#disallow_non_returning (fun () -> this#on_expr env rhs)
81 | Eif (e1, e2, e3) ->
82 this#disallow_non_returning (fun () -> this#on_expr env e1);
83 Option.iter e2 ~f:(this#on_expr env);
84 this#on_expr env e3
85 | Pipe (_, e1, e2) ->
86 this#disallow_non_returning (fun () -> this#on_expr env e1);
87 this#on_expr env e2
88 | List el -> List.iter el ~f:(this#on_expr env)
89 | ReadonlyExpr r ->
90 (* ReadonlyExprs can be immediately surrounding a void thing,
91 but the thing inside the expression should be checked for void *)
92 this#disallow_non_returning (fun () -> super#on_expr env r)
93 | ExpressionTree { et_class; et_runtime_expr } ->
94 this#on_id env et_class;
96 (* Allow calls to void functions at the top level:
98 Code`void_func()`
100 but not in subexpressions:
102 Code`() ==> { $x = void_func(); }`
104 this#on_expr env et_runtime_expr
105 | _ ->
106 if not !non_returning_allowed then check_valid_rvalue p env ty;
107 this#disallow_non_returning (fun () -> super#on_expr env te)
109 method! on_stmt env stmt =
110 match snd stmt with
111 | Expr e -> this#allow_non_returning (fun () -> this#on_expr env e)
112 | Return (Some (_, _, Hole (e, _, _, _)))
113 | Return (Some e) ->
114 this#allow_non_returning (fun () -> this#on_expr env e)
115 | For (e1, e2, e3, b) ->
116 this#allow_non_returning (fun () -> List.iter ~f:(this#on_expr env) e1);
117 this#disallow_non_returning (fun () ->
118 Option.iter ~f:(this#on_expr env) e2);
119 this#allow_non_returning (fun () -> List.iter ~f:(this#on_expr env) e3);
120 this#on_block env b
121 | Foreach (e1, e2, b) ->
122 this#disallow_non_returning (fun () -> this#on_expr env e1);
123 this#allow_non_returning (fun () -> this#on_as_expr env e2);
124 this#on_block env b
125 | Awaitall _ ->
126 this#allow_non_returning (fun () -> super#on_stmt env stmt)
127 | _ -> this#disallow_non_returning (fun () -> super#on_stmt env stmt)
129 method! on_block env block =
130 this#allow_non_returning (fun () -> super#on_block env block)
133 let handler =
134 object
135 inherit Tast_visitor.handler_base
137 method! at_stmt = visitor#on_stmt