/
statement.ml
6959 lines (6296 loc) · 268 KB
/
statement.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
module Ast = Flow_ast
module Tast_utils = Typed_ast_utils
(* This module contains the traversal functions which set up subtyping
constraints for every expression, statement, and declaration form in a
JavaScript AST; the subtyping constraints are themselves solved in module
Flow_js. It also manages environments, including not only the maintenance of
scope information for every function (pushing/popping scopes, looking up
variables) but also flow-sensitive information about local variables at every
point inside a function (and when to narrow or widen their types). *)
module Anno = Type_annotation
module Class_type_sig = Anno.Class_type_sig
module Object_freeze = Anno.Object_freeze
module Flow = Flow_js
module T = Type
open Utils_js
open Reason
open Type
open Env.LookupMode
open Destructuring
(*************)
(* Utilities *)
(*************)
let ident_name = Flow_ast_utils.name_of_ident
let mk_ident ~comments name = { Ast.Identifier.name; comments }
let snd_fst ((_, x), _) = x
let translate_identifier_or_literal_key t = Ast.Expression.Object.(function
| Property.Identifier (loc, name) -> Property.Identifier ((loc, t), name)
| Property.Literal (loc, lit) -> Property.Literal ((loc, t), lit)
| Property.PrivateName _ | Property.Computed _ -> assert_false "precondition not met")
let is_call_to_invariant callee =
match callee with
| (_, Ast.Expression.Identifier (_, { Ast.Identifier.name = "invariant"; _ })) -> true
| _ -> false
let convert_tparam_instantiations cx tparams_map instantiations =
let open Ast.Expression.TypeParameterInstantiation in
let rec loop ts tasts cx tparams_map = function
| [] -> (List.rev ts, List.rev tasts)
| ast::asts ->
begin match ast with
| Explicit ast ->
let (_, t), _ as tast = Anno.convert cx tparams_map ast in
loop ((ExplicitArg t)::ts) ((Explicit tast)::tasts) cx tparams_map asts
| Implicit loc ->
let reason = mk_reason RImplicitInstantiation loc in
let id = Tvar.mk_no_wrap cx reason in
loop ((ImplicitArg (reason, id))::ts)
((Implicit (loc, OpenT (reason, id)))::tasts) cx tparams_map asts
end
in
loop [] [] cx tparams_map instantiations
let convert_targs cx = function
| None -> None, None
| Some (loc, args) ->
let targts, targs_ast = convert_tparam_instantiations cx SMap.empty args in
Some targts, Some (loc, targs_ast)
class return_finder = object(this)
inherit [bool, ALoc.t] Flow_ast_visitor.visitor ~init:false as super
method! return _ node =
(* TODO we could pass over `return;` since it's definitely returning `undefined`. It will likely
* reposition existing errors from the `return;` to the location of the type annotation. *)
this#set_acc true;
node
method! call _loc expr =
begin if is_call_to_invariant Ast.Expression.Call.(expr.callee) then
this#set_acc true
end;
expr
method! throw _loc stmt =
this#set_acc true;
stmt
method! function_body_any body =
begin match body with
(* If it's a body expression, some value is implicitly returned *)
| Flow_ast.Function.BodyExpression _ -> this#set_acc true
| _ -> ()
end;
super#function_body_any body
(* Any returns in these constructs would be for nested function definitions, so we short-circuit
*)
method! class_ _ x = x
method! function_declaration _ x = x
end
let might_have_nonvoid_return loc function_ast =
let finder = new return_finder in
finder#eval (finder#function_ loc) function_ast
module Func_stmt_config = struct
type 'T ast = (ALoc.t, 'T) Ast.Function.Params.t
type 'T param_ast = (ALoc.t, 'T) Ast.Function.Param.t
type 'T rest_ast = (ALoc.t, 'T) Ast.Function.RestParam.t
type expr =
Context.t ->
(ALoc.t, ALoc.t) Flow_ast.Expression.t ->
(ALoc.t, ALoc.t * Type.t) Flow_ast.Expression.t
type pattern =
| Id of (ALoc.t, ALoc.t * Type.t) Ast.Pattern.Identifier.t
| Object of {
annot: (ALoc.t, ALoc.t * Type.t) Ast.Type.annotation_or_hint;
properties: (ALoc.t, ALoc.t) Ast.Pattern.Object.property list;
}
| Array of {
annot: (ALoc.t, ALoc.t * Type.t) Ast.Type.annotation_or_hint;
elements: (ALoc.t, ALoc.t) Ast.Pattern.Array.element option list;
}
type param = Param of {
t: Type.t;
loc: ALoc.t;
ploc: ALoc.t;
pattern: pattern;
default: (ALoc.t, ALoc.t) Ast.Expression.t option;
expr: expr;
}
type rest = Rest of {
t: Type.t;
loc: ALoc.t;
ploc: ALoc.t;
id: (ALoc.t, ALoc.t * Type.t) Ast.Pattern.Identifier.t;
}
let param_type (Param { t; pattern; default; _ }) =
match pattern with
| Id id ->
let { Ast.Pattern.Identifier.
name = (_, { Ast.Identifier.name; _ });
optional; _;
} = id in
let t = if optional || default <> None then Type.optional t else t in
Some name, t
| _ ->
let t = if default <> None then Type.optional t else t in
None, t
let rest_type (Rest { t; loc; id; _ }) =
let { Ast.Pattern.Identifier.
name = (_, { Ast.Identifier.name; _ });
_;
} = id in
Some name, loc, t
let subst_param cx map param =
let Param { t; loc; ploc; pattern; default; expr } = param in
let t = Flow.subst cx map t in
Param { t; loc; ploc; pattern; default; expr }
let subst_rest cx map rest =
let Rest { t; loc; ploc; id } = rest in
let t = Flow.subst cx map t in
Rest { t; loc; ploc; id }
let bind cx name t loc =
let open Scope in
if Context.enable_const_params cx
then
let kind = Entry.ConstParamBinding in
Env.bind_implicit_const ~state:State.Initialized
kind cx name t loc
else
let kind =
if Env.promote_to_const_like cx loc
then Entry.ConstlikeParamBinding
else Entry.ParamBinding
in
Env.bind_implicit_let ~state:State.Initialized
kind cx name t loc
let destruct cx annot ~use_op:_ loc name default t =
let reason = mk_reason (RIdentifier name) loc in
let t = match annot with
| Ast.Type.Missing _ -> t
| Ast.Type.Available _ ->
let source = Tvar.mk_where cx reason (fun t' ->
Flow.flow cx (t, BecomeT (reason, t'))
) in
AnnotT (reason, source, false)
in
Option.iter ~f:(fun d ->
let default_t = Flow.mk_default cx reason d in
Flow.flow_t cx (default_t, t)
) default;
bind cx name t loc
let eval_default cx ~expr = function
| None -> None
| Some e -> Some (expr cx e)
let eval_param cx (Param {t; loc; ploc; pattern; default; expr}) =
match pattern with
| Id id ->
let default = eval_default cx ~expr default in
let () =
match default with
| None -> ()
| Some ((_, default_t), _) -> Flow.flow_t cx (default_t, t)
in
let () =
let { Ast.Pattern.Identifier.
name = ((loc, _), { Ast.Identifier.name; _ });
optional; _
} = id in
let t = if optional && default = None then Type.optional t else t in
bind cx name t loc
in
loc, { Ast.Function.Param.
argument = ((ploc, t), Ast.Pattern.Identifier id);
default;
}
| Object { annot; properties } ->
let default = eval_default cx ~expr default in
let properties =
let default = Option.map default (fun ((_, t), _) ->
Default.expr t
) in
let init = Destructuring.empty ?default t in
let f = destruct cx annot in
Destructuring.object_properties cx ~expr ~f init properties
in
loc, { Ast.Function.Param.
argument = ((ploc, t), Ast.Pattern.Object { Ast.Pattern.Object.
properties;
annot;
});
default;
}
| Array { annot; elements } ->
let default = eval_default cx ~expr default in
let elements =
let default = Option.map default (fun ((_, t), _) ->
Default.expr t
) in
let init = Destructuring.empty ?default t in
let f = destruct cx annot in
Destructuring.array_elements cx ~expr ~f init elements
in
loc, { Ast.Function.Param.
argument = ((ploc, t), Ast.Pattern.Array { Ast.Pattern.Array.
elements;
annot;
});
default;
}
let eval_rest cx (Rest {t; loc; ploc; id}) =
let () =
let { Ast.Pattern.Identifier.
name = ((loc, _), { Ast.Identifier.name; _ });
_
} = id in
bind cx name t loc
in
loc, { Ast.Function.RestParam.
argument = ((ploc, t), Ast.Pattern.Identifier id)
}
end
module Func_stmt_params = Func_params.Make (Func_stmt_config)
module Func_stmt_sig = Func_sig.Make (Func_stmt_params)
module Class_stmt_sig = Class_sig.Make (Func_stmt_sig)
module Class_property = struct
type t =
| Public of string
| Private of string
let compare = compare
end
module Class_property_map = Map.Make(Class_property)
(************)
(* Visitors *)
(************)
(********************************************************************
* local inference preliminary pass: traverse AST, collecting
* declarations and populating variable environment (scope stack)
* in prep for main pass
********************************************************************)
let rec variable_decl cx { Ast.Statement.VariableDeclaration.kind; declarations } =
let bind = match kind with
| Ast.Statement.VariableDeclaration.Const -> Env.bind_const
| Ast.Statement.VariableDeclaration.Let -> Env.bind_let
| Ast.Statement.VariableDeclaration.Var -> Env.bind_var
in
Flow_ast_utils.fold_bindings_of_variable_declarations (
fun () (loc, { Ast.Identifier.name; comments = _ }) ->
let reason = mk_reason (RIdentifier name) loc in
let t = Tvar.mk cx reason in
bind cx name t loc
) () declarations
and toplevel_decls cx =
List.iter (statement_decl cx)
(* TODO: detect structural misuses abnormal control flow constructs *)
and statement_decl cx = Ast.Statement.(
let block_body cx { Block.body } =
Env.in_lex_scope cx (fun () ->
toplevel_decls cx body
)
in
let catch_clause cx { Try.CatchClause.body = (_, b); _ } =
block_body cx b
in
function
| (_, Empty) -> ()
| (_, Block b) ->
block_body cx b
| (_, Expression _) -> ()
| (_, If { If.consequent; alternate; _ }) ->
statement_decl cx consequent;
(match alternate with
| None -> ()
| Some st -> statement_decl cx st
)
| (_, Labeled { Labeled.body; _ }) ->
statement_decl cx body
| (_, Break _) -> ()
| (_, Continue _) -> ()
| (_, With _) ->
(* TODO disallow or push vars into env? *)
()
| (_, DeclareTypeAlias { TypeAlias.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } )
| (_, TypeAlias { TypeAlias.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } ) ->
let r = DescFormat.type_reason name (name_loc) in
let tvar = Tvar.mk cx r in
Env.bind_type cx name tvar name_loc
| (_, DeclareOpaqueType { OpaqueType.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } )
| (_, OpaqueType { OpaqueType.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } ) ->
let r = DescFormat.type_reason name name_loc in
let tvar = Tvar.mk cx r in
Env.bind_type cx name tvar name_loc
| (_, Switch { Switch.cases; _ }) ->
Env.in_lex_scope cx (fun () ->
cases |> List.iter (fun (_, { Switch.Case.consequent; _ }) ->
toplevel_decls cx consequent
)
)
| (_, Return _) -> ()
| (_, Throw _) -> ()
| (_, Try { Try.block = (_, b); handler; finalizer }) ->
block_body cx b;
(match handler with
| None -> ()
| Some (_, h) -> catch_clause cx h
);
(match finalizer with
| None -> ()
| Some (_, b) -> block_body cx b
)
| (_, While { While.body; _ }) ->
statement_decl cx body
| (_, DoWhile { DoWhile.body; _ }) ->
statement_decl cx body
| (_, For { For.init; body; _ }) ->
Env.in_lex_scope cx (fun () ->
(match init with
| Some (For.InitDeclaration (_, decl)) ->
variable_decl cx decl
| _ -> ()
);
statement_decl cx body
)
| (_, ForIn { ForIn.left; body; _ }) ->
Env.in_lex_scope cx (fun () ->
(match left with
| ForIn.LeftDeclaration (_, decl) ->
variable_decl cx decl
| _ -> ()
);
statement_decl cx body
)
| (_, ForOf { ForOf.left; body; _ }) ->
Env.in_lex_scope cx (fun () ->
(match left with
| ForOf.LeftDeclaration (_, decl) ->
variable_decl cx decl
| _ -> ()
);
statement_decl cx body
)
| (_, Debugger) -> ()
| (loc, FunctionDeclaration { Ast.Function.id; async; generator; _ }) ->
(match id with
| Some (_, { Ast.Identifier.name; comments= _ }) ->
let r = func_reason ~async ~generator loc in
let tvar = Tvar.mk cx r in
Env.bind_fun cx name tvar loc
| None ->
failwith (
"Flow Error: Nameless function declarations should always be given " ^
"an implicit name before they get hoisted!"
)
)
| _, EnumDeclaration _ -> ()
| (loc, DeclareVariable { DeclareVariable.id = (id_loc, { Ast.Identifier.name; comments= _ }); _ }) ->
let r = mk_reason (RCustom (spf "declare %s" name)) loc in
let t = Tvar.mk cx r in
Env.bind_declare_var cx name t id_loc
| (loc, DeclareFunction ({ DeclareFunction.
id = (id_loc, { Ast.Identifier.name; comments= _ });
_; } as declare_function)) ->
(match declare_function_to_function_declaration cx loc declare_function with
| None ->
let r = mk_reason (RCustom (spf "declare %s" name)) loc in
let t = Tvar.mk cx r in
Env.bind_declare_fun cx name t id_loc
| Some (func_decl, _) ->
statement_decl cx (loc, func_decl)
)
| (_, VariableDeclaration decl) ->
variable_decl cx decl
| (_, ClassDeclaration { Ast.Class.id; _ }) -> (
match id with
| Some (name_loc, { Ast.Identifier.name; comments= _ }) ->
let r = mk_reason (RType name) name_loc in
let tvar = Tvar.mk cx r in
Env.bind_implicit_let Scope.Entry.ClassNameBinding cx name tvar name_loc
| None -> ()
)
| (_, DeclareClass { DeclareClass.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ })
| (_, DeclareInterface { Interface.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ })
| (_, InterfaceDeclaration { Interface.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ }) as stmt ->
let is_interface = match stmt with
| (_, DeclareInterface _) -> true
| (_, InterfaceDeclaration _) -> true
| _ -> false in
let r = mk_reason (RType name) name_loc in
let tvar = Tvar.mk cx r in
(* interface is a type alias, declare class is a var *)
if is_interface
then Env.bind_type cx name tvar name_loc
else Env.bind_declare_var cx name tvar name_loc
| (loc, DeclareModule { DeclareModule.id; _ }) ->
let name = match id with
| DeclareModule.Identifier (_, { Ast.Identifier.name= value; comments= _ })
| DeclareModule.Literal (_, { Ast.StringLiteral.value; _ }) -> value
in
let r = mk_reason (RModule name) loc in
let t = Tvar.mk cx r in
Env.bind_declare_var cx (internal_module_name name) t loc
| _,
DeclareExportDeclaration {
DeclareExportDeclaration.default; declaration; _
} ->
DeclareExportDeclaration.(match declaration with
| Some (Variable (loc, v)) ->
statement_decl cx (loc, DeclareVariable v)
| Some (Function (loc, f)) ->
statement_decl cx (loc, DeclareFunction f)
| Some (Class (loc, c)) ->
statement_decl cx (loc, DeclareClass c)
| Some (DefaultType _) -> ()
| Some (NamedType (loc, t)) ->
statement_decl cx (loc, TypeAlias t)
| Some (NamedOpaqueType (loc, t)) ->
statement_decl cx (loc, OpaqueType t)
| Some (Interface (loc, i)) ->
statement_decl cx (loc, InterfaceDeclaration i)
| None ->
if Option.is_none default
then ()
else failwith (
"Parser Error: declare export default must always have an " ^
"associated declaration or type!"
)
)
| (_, DeclareModuleExports _) -> ()
| (_, ExportNamedDeclaration { ExportNamedDeclaration.declaration; _ }) -> (
match declaration with
| Some stmt -> statement_decl cx stmt
| None -> ()
)
| _, ExportDefaultDeclaration { ExportDefaultDeclaration.declaration; _ } -> (
match declaration with
| ExportDefaultDeclaration.Declaration stmt ->
let stmt, _ = Import_export.nameify_default_export_decl stmt in
statement_decl cx stmt
| ExportDefaultDeclaration.Expression _ -> ()
)
| (_, ImportDeclaration { ImportDeclaration.importKind; specifiers; default; source = _ }) ->
let isType =
match importKind with
| ImportDeclaration.ImportType -> true
| ImportDeclaration.ImportTypeof -> true
| ImportDeclaration.ImportValue -> false
in
let bind_import local_name (loc: ALoc.t) isType =
let reason = if isType
then DescFormat.type_reason local_name loc
else mk_reason (RIdentifier local_name) loc in
let tvar = Tvar.mk cx reason in
if isType
then Env.bind_import_type cx local_name tvar loc
else Env.bind_import cx local_name tvar loc
in
Option.iter ~f:(fun local ->
bind_import (ident_name local) (fst local) isType
) default;
Option.iter ~f:(function
| ImportDeclaration.ImportNamespaceSpecifier (_, local) ->
bind_import (ident_name local) (fst local) isType
| ImportDeclaration.ImportNamedSpecifiers named_specifiers ->
List.iter (fun { ImportDeclaration.local; remote; kind;} ->
let (loc, { Ast.Identifier.name= local_name; comments= _ }) = Option.value ~default:remote local in
let isType = isType || (
match kind with
| None -> isType
| Some kind ->
kind = ImportDeclaration.ImportType
|| kind = ImportDeclaration.ImportTypeof
) in
bind_import local_name loc isType
) named_specifiers
) specifiers
)
(***************************************************************
* local inference main pass: visit AST statement list, calling
* flow to check types/create graphs for merge-time checking
***************************************************************)
(* accumulates a list of previous statements' ASTs in reverse order *)
(* can raise Abnormal.(Exn (Stmts _, _)). *)
and toplevels =
let rec loop acc cx = function
| [] -> List.rev acc
| (loc, Ast.Statement.Empty)::stmts ->
loop ((loc, Ast.Statement.Empty)::acc) cx stmts
| stmt::stmts ->
match Abnormal.catch_stmt_control_flow_exception (fun () -> statement cx stmt) with
| stmt, Some abnormal ->
(* control flow exit out of a flat list:
check for unreachable code and rethrow *)
let warn_unreachable loc =
Flow.add_output cx (Error_message.EUnreachable loc) in
let rest = Core_list.map ~f:Ast.Statement.(fun stmt ->
match stmt with
| (_, Empty) as stmt -> stmt
(* function declarations are hoisted, so not unreachable *)
| (_, FunctionDeclaration _ ) -> statement cx stmt
(* variable declarations are hoisted, but associated assignments are
not, so skip variable declarations with no assignments.
Note: this does not seem like a practice anyone would use *)
| (_, VariableDeclaration d) as stmt -> VariableDeclaration.(d.declarations |>
List.iter Declarator.(function
| (_, { init = Some (loc, _); _ } ) -> warn_unreachable loc
| _ -> ()
));
Tast_utils.unreachable_mapper#statement stmt
| (loc, _) as stmt ->
warn_unreachable loc;
Tast_utils.unreachable_mapper#statement stmt
) stmts in
Abnormal.throw_stmts_control_flow_exception
(List.rev_append acc (stmt::rest))
abnormal
| stmt, None -> loop (stmt::acc) cx stmts
in
fun cx -> loop [] cx
(* can raise Abnormal.(Exn (Stmt _, _)) *)
and statement cx : 'a -> (ALoc.t, ALoc.t * Type.t) Ast.Statement.t = Ast.Statement.(
let variables cx decls =
let open VariableDeclaration in
let { declarations; kind } = decls in
let declarations = Core_list.map ~f:(fun (loc, { Declarator.id; init }) ->
let id, init = variable cx kind id init in
(loc, { Declarator.id; init })
) declarations in
{ declarations; kind; }
in
let interface_helper cx loc (iface_sig, self) =
let def_reason = mk_reason (desc_of_t self) loc in
Class_type_sig.generate_tests cx (fun iface_sig ->
Class_type_sig.check_super cx def_reason iface_sig;
Class_type_sig.check_implements cx def_reason iface_sig
) iface_sig;
let t = Class_type_sig.classtype ~check_polarity:false cx iface_sig in
Flow.unify cx self t;
t
in
let interface cx loc decl =
let { Interface.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } = decl in
let reason = DescFormat.instance_reason name name_loc in
let iface_sig, iface_t, decl_ast = Anno.mk_interface_sig cx reason decl in
let t = interface_helper cx loc (iface_sig, iface_t) in
Env.init_type cx name t loc;
decl_ast
in
let declare_class cx loc decl =
let { DeclareClass.id = (name_loc, { Ast.Identifier.name; comments= _ }); _ } = decl in
let reason = DescFormat.instance_reason name name_loc in
let class_sig, class_t, decl_ast = Anno.mk_declare_class_sig cx reason decl in
let t = interface_helper cx loc (class_sig, class_t) in
let use_op = Op (AssignVar {
var = Some (mk_reason (RIdentifier name) loc);
init = reason_of_t t;
}) in
Env.init_var ~has_anno:false cx ~use_op name t loc;
decl_ast
in
let check cx b = Abnormal.catch_stmts_control_flow_exception(fun () ->
toplevel_decls cx b.Block.body;
toplevels cx b.Block.body) in
let catch_clause cx catch_clause =
let { Try.CatchClause.param; body = (b_loc, b) } = catch_clause in
Ast.Pattern.(match param with
| Some p -> (match p with
| loc, Identifier {
Identifier.name = name_loc, ({ Ast.Identifier.name; comments= _ } as id);
annot = Ast.Type.Missing mloc;
optional;
} ->
let r = mk_reason (RCustom "catch") loc in
let t = Tvar.mk cx r in
let stmts, abnormal_opt = Env.in_lex_scope cx (fun () ->
Scope.(Env.bind_implicit_let
~state:State.Initialized Entry.CatchParamBinding cx name t loc);
check cx b
) in
{ Try.CatchClause.
param = Some ((loc, t), Ast.Pattern.Identifier { Ast.Pattern.Identifier.
name = (name_loc, t), id;
annot = Ast.Type.Missing (mloc, t);
optional;
});
body = b_loc, { Block.body = stmts };
},
abnormal_opt
| loc, Identifier _ ->
Flow.add_output cx
Error_message.(EUnsupportedSyntax (loc, CatchParameterAnnotation));
Tast_utils.error_mapper#catch_clause catch_clause, None
| loc, _ ->
Flow.add_output cx
Error_message.(EUnsupportedSyntax (loc, CatchParameterDeclaration));
Tast_utils.error_mapper#catch_clause catch_clause, None
)
| None ->
let stmts, abnormal_opt = Env.in_lex_scope cx (fun () ->
check cx b
) in
{ Try.CatchClause.
param = None;
body = b_loc, { Block.body = stmts };
},
abnormal_opt
)
in
function
| (_, Empty) as stmt -> stmt
| (loc, Block { Block.body }) ->
let body, abnormal_opt =
Abnormal.catch_stmts_control_flow_exception (fun () ->
Env.in_lex_scope cx (fun () ->
toplevel_decls cx body;
toplevels cx body
)
)
in
Abnormal.check_stmt_control_flow_exception (
(loc, Block { Block.body }),
abnormal_opt
)
| (loc, Expression { Expression.expression = e; directive; }) ->
loc, Expression { Expression.
expression = expression cx e;
directive;
}
(* Refinements for `if` are derived by the following Hoare logic rule:
[Pre & c] S1 [Post1]
[Pre & ~c] S2 [Post2]
Post = Post1 | Post2
----------------------------
[Pre] if c S1 else S2 [Post]
*)
| (loc, If { If.test; consequent; alternate }) ->
let loc_test, _ = test in
let test_ast, preds, not_preds, xts =
predicates_of_condition cx test in
(* grab a reference to the incoming env -
we'll restore it and merge branched envs later *)
let start_env = Env.peek_env () in
let oldset = Changeset.Global.clear () in
(* swap in a refined clone of initial env for then *)
Env.(
update_env cx loc (clone_env start_env);
ignore (refine_with_preds cx loc_test preds xts)
);
let then_ast, then_abnormal = Abnormal.catch_stmt_control_flow_exception
(fun () -> statement cx consequent)
in
(* grab a reference to env after then branch *)
let then_env = Env.peek_env () in
(* then swap in a refined clone of initial env for else *)
Env.(
update_env cx loc (clone_env start_env);
ignore (refine_with_preds cx loc_test not_preds xts)
);
let else_ast, else_abnormal = match alternate with
| None -> None, None
| Some st ->
let else_ast, else_abnormal =
Abnormal.catch_stmt_control_flow_exception
(fun () -> statement cx st)
in Some else_ast, else_abnormal
in
(* grab a reference to env after else branch *)
let else_env = Env.peek_env () in
(* snapshot if-else changes and merge old changes back into state *)
let newset = Changeset.Global.merge oldset in
(* adjust post-if environment. if we've returned from one arm,
swap in the env generated by the other, otherwise merge *)
let end_env = match then_abnormal, else_abnormal with
| Some Abnormal.Return, None
| Some Abnormal.Throw, None ->
else_env
| None, Some Abnormal.Return
| None, Some Abnormal.Throw ->
then_env
| None, Some _
| Some _, None
| Some _, Some _ ->
Env.merge_env cx loc (start_env, then_env, else_env) newset;
start_env
| None, None ->
(* if neither branch has abnormal flow, then refinements that happen in
the branches should be forgotten since the original type covers
all of the options. *)
Env.merge_env cx loc
(start_env, then_env, else_env)
(Changeset.exclude_refines newset);
start_env
in
Env.update_env cx loc end_env;
let ast = loc, If { If.
test = test_ast;
consequent = then_ast;
alternate = else_ast;
} in
(* handle control flow in cases where we've thrown from both sides *)
begin match then_abnormal, else_abnormal with
| Some Abnormal.Throw, Some Abnormal.Return
| Some Abnormal.Return, Some Abnormal.Throw ->
Abnormal.throw_stmt_control_flow_exception ast Abnormal.Return;
| Some then_exn, Some else_exn when then_exn = else_exn ->
Abnormal.throw_stmt_control_flow_exception ast then_exn
| _ -> ast
end
| (top_loc, Labeled { Labeled.label = _, { Ast.Identifier.name; comments= _ } as lab_ast; body }) ->
(match body with
| (loc, While _)
| (loc, DoWhile _)
| (loc, For _)
| (loc, ForIn _)
->
let oldset = Changeset.Global.clear () in
let label = Some name in
let save_break = Abnormal.clear_saved (Abnormal.Break label) in
let save_continue = Abnormal.clear_saved (Abnormal.Continue label) in
let env = Env.peek_env () in
Env.widen_env cx loc;
let loop_env = Env.clone_env env in
Env.update_env cx loc loop_env;
let body_ast, body_abnormal =
Abnormal.catch_stmt_control_flow_exception (fun () -> statement cx body)
|> Abnormal.ignore_break_or_continue_to_label label
in
let ast = top_loc, Labeled { Labeled.label = lab_ast; body = body_ast } in
ignore (Abnormal.check_stmt_control_flow_exception (ast, body_abnormal)
: (ALoc.t, ALoc.t * Type.t) Ast.Statement.t);
let newset = Changeset.Global.merge oldset in
if Abnormal.swap_saved (Abnormal.Continue label) save_continue <> None
then Env.havoc_vars newset;
Env.copy_env cx loc (env,loop_env) newset;
if Abnormal.swap_saved (Abnormal.Break label) save_break <> None
then Env.havoc_vars newset;
ast
| _ ->
let oldset = Changeset.Global.clear () in
let label = Some name in
let save_break = Abnormal.clear_saved (Abnormal.Break label) in
let body_ast, body_abnormal =
Abnormal.catch_stmt_control_flow_exception (fun () -> statement cx body)
|> Abnormal.ignore_break_to_label label
in
let ast = top_loc, Labeled { Labeled.label = lab_ast; body = body_ast } in
ignore (Abnormal.check_stmt_control_flow_exception (ast, body_abnormal)
: (ALoc.t, ALoc.t * Type.t) Ast.Statement.t);
let newset = Changeset.Global.merge oldset in
if Abnormal.swap_saved (Abnormal.Break label) save_break <> None
then Env.havoc_vars newset;
ast
)
| (loc, Break { Break.label }) ->
(* save environment at unlabeled breaks, prior to activation clearing *)
let label_opt, env, label_ast = match label with
| None -> None, Env.(clone_env (peek_env ())), None
| Some (_, { Ast.Identifier.name; comments= _ } as lab_ast) -> Some name, [], Some lab_ast
in
Env.reset_current_activation loc;
let ast = loc, Break { Break.label = label_ast } in
let abnormal = Abnormal.Break label_opt in
Abnormal.save abnormal ~env;
Abnormal.throw_stmt_control_flow_exception ast abnormal
| (loc, Continue { Continue.label; comments }) ->
let label_opt, label_ast = match label with
| None -> None, None
| Some (_, { Ast.Identifier.name; comments= _ } as lab_ast) -> Some name, Some lab_ast
in
Env.reset_current_activation loc;
let ast = loc, Continue { Continue.label = label_ast; comments = comments } in
let abnormal = Abnormal.Continue label_opt in
Abnormal.save abnormal;
Abnormal.throw_stmt_control_flow_exception ast abnormal
| (_, With _) as s ->
(* TODO or disallow? *)
Tast_utils.error_mapper#statement s
|((loc, DeclareTypeAlias {TypeAlias.id=name_loc, ({ Ast.Identifier.name; comments= _ } as id); tparams; right;})
| (loc, TypeAlias {TypeAlias.id=name_loc, ({ Ast.Identifier.name; comments= _ } as id); tparams; right;})) as stmt ->
let r = DescFormat.type_reason name name_loc in
let typeparams, typeparams_map, tparams_ast =
Anno.mk_type_param_declarations cx tparams in
let (_, t), _ as right_ast = Anno.convert cx typeparams_map right in
let t =
let mod_reason = replace_reason ~keep_def_loc:true
(fun desc -> RTypeAlias (name, true, desc)) in
let rec loop = function
| ExactT (r, t) -> ExactT (mod_reason r, loop t)
| MaybeT (r, t) -> MaybeT (mod_reason r, loop t)
| t -> mod_reason_of_t mod_reason t
in
loop t
in
let type_ = poly_type_of_tparams (Context.make_nominal cx) typeparams
(DefT (r, bogus_trust (), TypeT (TypeAliasKind, t))) in
Flow.check_polarity cx Polarity.Positive t;
Env.init_type cx name type_ name_loc;
let type_alias_ast = { TypeAlias.
id = (name_loc, type_), id;
tparams = tparams_ast;
right = right_ast;
} in
(match stmt with
| _, DeclareTypeAlias _ -> loc, DeclareTypeAlias type_alias_ast
| _, TypeAlias _ -> loc, TypeAlias type_alias_ast
| _ -> assert false)
|((loc, DeclareOpaqueType
{OpaqueType.id=name_loc, ({ Ast.Identifier.name; comments= _ } as id); tparams; impltype; supertype})
| (loc, OpaqueType {OpaqueType.id=name_loc, ({ Ast.Identifier.name; comments= _ } as id); tparams; impltype; supertype}))
as stmt ->
let r = DescFormat.type_reason name name_loc in
let typeparams, typeparams_map, tparams_ast =
Anno.mk_type_param_declarations cx tparams in
let underlying_t, impltype_ast = Anno.convert_opt cx typeparams_map impltype in
let opaque_type_args = Core_list.map ~f:(fun {name; reason; polarity; _} ->
let t = SMap.find_unsafe name typeparams_map in
name, reason, t, polarity
) (TypeParams.to_list typeparams) in
let super_t, supertype_ast = Anno.convert_opt cx typeparams_map supertype in
let opaquetype = {
underlying_t;
super_t;
opaque_id = name_loc;
opaque_type_args;
opaque_name = name
} in
let t = OpaqueT (mk_reason (ROpaqueType name) loc, opaquetype) in
Flow.check_polarity cx Polarity.Positive t;
let type_ = poly_type_of_tparams (Context.make_nominal cx) typeparams
(DefT (r, bogus_trust (), TypeT (OpaqueKind, t))) in
let open Flow in
let () = match underlying_t, super_t with
| Some l, Some u ->
generate_tests cx (TypeParams.to_list typeparams) (fun map_ ->
flow_t cx (subst cx map_ l, subst cx map_ u)
) |> ignore
| _ -> ()