tree-sitter-ocaml 0.19.0

OCaml grammar for the tree-sitter parsing library
Documentation
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
(*
 * 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

(* This module defines a general notion of trace, which is used in modules
   Type_inference_js and Flow_js to record how the typechecker reasons about
   code, systematically collecting, simplifying, and solving constraints. This
   is extremely useful, not only for debugging the typechecker but also to
   really understand why an error is reported. *)

(* Eventually, trace information should be printed out only in verbose mode,
   since Flow reports all errors it finds and the trace for every error can get
   quite detailed dependening on how far apart the "source" and "sink" are and
   how convoluted the flow between them is. *)

open Utils_js
open String_utils

let mk_id () = Ident.make ""

(* Reasons are included in types mainly for error reporting, but sometimes we
   also use reasons in types to recover information on the source code that
   caused those reasons to be created. Two examples of such secondary uses of
   reasons are:

   - strictness analysis: we use reasons to locate the origin of an object and
   the origin of an operation on the object, and use such origins to determine
   whether certain errors should be suppressed.

   - termination analysis: we use reasons to limit instantiation of type
   parameters in polymorphic types at particular locations, to prevent the type
   checker from generating an unbounded number of constraints. The `pos` field
   of reasons is sufficient to distinguish code locations, except that as an
   implementation strategy for checking polymorphic definitions, we walk over
   the same source code multiple times to check it with different instantiations
   of type parameters, and to index "copies" of the reasons created in those
   passes over the same source code, we use an additional `test_id` field.
*)
module TestID = struct
  let _current = ref None

  (* Get current test id. *)
  let current () = !_current

  (* Call f on a, installing new_test_id as the current test_id, and restoring
     the current test_id when done. (See also the function mk_reason below.) *)
  let run f a =
    let test_id = current () in
    _current := Some (mk_id ());
    let b = f a in
    _current := test_id;
    b
end

type 'loc virtual_reason_desc =
  | RTrusted of 'loc virtual_reason_desc
  | RPrivate of 'loc virtual_reason_desc
  | RAnyExplicit
  | RAnyImplicit
  | RNumber
  | RBigInt
  | RString
  | RBoolean
  | RMixed
  | REmpty
  | RVoid
  | RNull
  | RVoidedNull
  | RSymbol
  | RExports
  | RNullOrVoid
  | RLongStringLit of int (* Max length *)
  | RStringLit of string
  | RNumberLit of string
  | RBigIntLit of string
  | RBooleanLit of bool
  | RMatchingProp of string * 'loc virtual_reason_desc
  | RObject
  | RObjectLit
  | RObjectType
  | RObjectClassName
  | RInterfaceType
  | RArray
  | RArrayLit
  | REmptyArrayLit
  | RArrayType
  | RROArrayType
  | RTupleType
  | RTupleElement
  | RTupleLength of int
  | RTupleOutOfBoundsAccess
  | RFunction of reason_desc_function
  | RFunctionType
  | RFunctionBody
  | RFunctionCall of 'loc virtual_reason_desc
  | RFunctionCallType
  | RFunctionUnusedArgument
  | RJSXFunctionCall of string
  | RJSXIdentifier of string * string
  | RJSXElementProps of string
  | RJSXElement of string option
  | RJSXText
  | RFbt
  | RUnaryOperator of string * 'loc virtual_reason_desc
  | RBinaryOperator of string * 'loc virtual_reason_desc * 'loc virtual_reason_desc
  | RLogical of string * 'loc virtual_reason_desc * 'loc virtual_reason_desc
  | RTemplateString
  | RUnknownString
  | RUnionEnum
  | REnum of string (* name *)
  | REnumRepresentation of 'loc virtual_reason_desc
  | RGetterSetterProperty
  | RThis
  | RThisType
  | RExistential
  | RImplicitInstantiation
  | RTooFewArgs
  | RTooFewArgsExpectedRest
  | RConstructorReturn
  | RNewObject
  | RUnion
  | RUnionType
  | RIntersection
  | RIntersectionType
  | RKeySet
  | RAnd
  | RConditional
  | RPrototype
  | RObjectPrototype
  | RFunctionPrototype
  | RDestructuring
  | RDefaultValue
  | RConstructor
  | RDefaultConstructor
  | RConstructorCall of 'loc virtual_reason_desc
  | RReturn
  | RImplicitReturn of 'loc virtual_reason_desc
  | RRegExp
  | RSuper
  | RNoSuper
  | RDummyPrototype
  | RDummyThis
  | RTupleMap
  | RObjectMap
  | RObjectMapi
  | RType of string
  | RTypeAlias of string * 'loc option (* reliable def loc *) * 'loc virtual_reason_desc
  | ROpaqueType of string
  | RTypeParam of
      string * ('loc virtual_reason_desc * 'loc) * (*reason op *)
      ('loc virtual_reason_desc * 'loc) (* reason tapp *)
  | RTypeof of string
  | RMethod of string option
  | RMethodCall of string option
  | RParameter of string option
  | RRestParameter of string option
  | RIdentifier of string
  | RIdentifierAssignment of string
  | RPropertyAssignment of string option
  | RProperty of string option
  | RPrivateProperty of string
  | RShadowProperty of string
  | RMember of {
      object_: string;
      property: string;
    }
  | RPropertyOf of string * 'loc virtual_reason_desc
  | RPropertyIsAString of string
  | RMissingProperty of string option
  | RUnknownProperty of string option
  | RUndefinedProperty of string
  | RSomeProperty
  | RNameProperty of 'loc virtual_reason_desc
  | RMissingAbstract of 'loc virtual_reason_desc
  | RFieldInitializer of string
  | RUntypedModule of string
  | RNamedImportedType of string (* module *) * string (* local name *)
  | RImportStarType of string
  | RImportStarTypeOf of string
  | RImportStar of string
  | RDefaultImportedType of string * string
  | RAsyncImport
  | RCode of string
  | RCustom of string
  | RPolyType of 'loc virtual_reason_desc
  | RPolyTest of string * 'loc virtual_reason_desc
  | RExactType of 'loc virtual_reason_desc
  | ROptional of 'loc virtual_reason_desc
  | RMaybe of 'loc virtual_reason_desc
  | RRestArray of 'loc virtual_reason_desc
  | RAbstract of 'loc virtual_reason_desc
  | RTypeApp of 'loc virtual_reason_desc
  | RTypeAppImplicit of 'loc virtual_reason_desc
  | RThisTypeApp of 'loc virtual_reason_desc
  | RExtends of 'loc virtual_reason_desc
  | RClass of 'loc virtual_reason_desc
  | RStatics of 'loc virtual_reason_desc
  | RSuperOf of 'loc virtual_reason_desc
  | RFrozen of 'loc virtual_reason_desc
  | RBound of 'loc virtual_reason_desc
  | RPredicateOf of 'loc virtual_reason_desc
  | RPredicateCall of 'loc virtual_reason_desc
  | RPredicateCallNeg of 'loc virtual_reason_desc
  | RRefined of 'loc virtual_reason_desc
  | RIncompatibleInstantiation of string
  | RSpreadOf of 'loc virtual_reason_desc
  | RShapeOf of 'loc virtual_reason_desc
  | RObjectPatternRestProp
  | RArrayPatternRestProp
  | RCommonJSExports of string
  | RModule of string
  | ROptionalChain
  | RReactProps
  | RReactElement of string option
  | RReactClass
  | RReactComponent
  | RReactStatics
  | RReactDefaultProps
  | RReactState
  | RReactPropTypes
  | RReactChildren
  | RReactChildrenOrType of 'loc virtual_reason_desc
  | RReactChildrenOrUndefinedOrType of 'loc virtual_reason_desc
  | RReactSFC
  | RReactConfig
  | RPossiblyMissingPropFromObj of string * 'loc virtual_reason_desc
  | RWidenedObjProp of 'loc virtual_reason_desc
  | RUnionBranching of 'loc virtual_reason_desc * int
[@@deriving eq, show]

and reason_desc_function =
  | RAsync
  | RGenerator
  | RAsyncGenerator
  | RNormal
  | RUnknown

type reason_desc = ALoc.t virtual_reason_desc

let rec map_desc_locs f = function
  | ( RAnyExplicit | RAnyImplicit | RNumber | RBigInt | RString | RBoolean | RMixed | REmpty | RVoid
    | RNull | RVoidedNull | RSymbol | RExports | RNullOrVoid | RLongStringLit _ | RStringLit _
    | RNumberLit _ | RBigIntLit _ | RBooleanLit _ | RObject | RObjectLit | RObjectType
    | RObjectClassName | RInterfaceType | RArray | RArrayLit | REmptyArrayLit | RArrayType
    | RROArrayType | RTupleType | RTupleElement | RTupleLength _ | RTupleOutOfBoundsAccess
    | RFunction _ | RFunctionType | RFunctionBody | RFunctionCallType | RFunctionUnusedArgument
    | RJSXFunctionCall _ | RJSXIdentifier _ | RJSXElementProps _ | RJSXElement _ | RJSXText | RFbt
      ) as r ->
    r
  | RFunctionCall desc -> RFunctionCall (map_desc_locs f desc)
  | RUnaryOperator (s, desc) -> RUnaryOperator (s, map_desc_locs f desc)
  | RBinaryOperator (s, d1, d2) -> RBinaryOperator (s, map_desc_locs f d1, map_desc_locs f d2)
  | RLogical (s, d1, d2) -> RLogical (s, map_desc_locs f d1, map_desc_locs f d2)
  | ( RTemplateString | RUnknownString | RUnionEnum | REnum _ | RGetterSetterProperty | RThis
    | RThisType | RExistential | RImplicitInstantiation | RTooFewArgs | RTooFewArgsExpectedRest
    | RConstructorReturn | RNewObject | RUnion | RUnionType | RIntersection | RIntersectionType
    | RKeySet | RAnd | RConditional | RPrototype | RObjectPrototype | RFunctionPrototype
    | RDestructuring | RDefaultValue | RConstructor | RReturn | RDefaultConstructor | RRegExp
    | RSuper | RNoSuper | RDummyPrototype | RDummyThis | RTupleMap | RObjectMap | RType _
    | RTypeof _ | RMethod _ | RMethodCall _ | RParameter _ | RRestParameter _ | RIdentifier _
    | RIdentifierAssignment _ | RPropertyAssignment _ | RProperty _ | RPrivateProperty _
    | RShadowProperty _ | RMember _ | RPropertyIsAString _ | RMissingProperty _ | RUnknownProperty _
    | RUndefinedProperty _ | RSomeProperty | RFieldInitializer _ | RUntypedModule _
    | RNamedImportedType _ | RImportStarType _ | RImportStarTypeOf _ | RImportStar _
    | RDefaultImportedType _ | RAsyncImport | RCode _ | RCustom _ | RIncompatibleInstantiation _
    | ROpaqueType _ | RObjectMapi ) as r ->
    r
  | REnumRepresentation desc -> REnumRepresentation (map_desc_locs f desc)
  | RConstructorCall desc -> RConstructorCall (map_desc_locs f desc)
  | RImplicitReturn desc -> RImplicitReturn (map_desc_locs f desc)
  | RTypeAlias (s, None, d) -> RTypeAlias (s, None, map_desc_locs f d)
  | RTypeAlias (s, Some b, d) -> RTypeAlias (s, Some (f b), map_desc_locs f d)
  | RTypeParam (s, (d1, l1), (d2, l2)) ->
    RTypeParam (s, (map_desc_locs f d1, f l1), (map_desc_locs f d2, f l2))
  | RPropertyOf (s, d) -> RPropertyOf (s, map_desc_locs f d)
  | RNameProperty desc -> RNameProperty (map_desc_locs f desc)
  | RMissingAbstract desc -> RMissingAbstract (map_desc_locs f desc)
  | RPolyType desc -> RPolyType (map_desc_locs f desc)
  | RPolyTest (s, desc) -> RPolyTest (s, map_desc_locs f desc)
  | RExactType desc -> RExactType (map_desc_locs f desc)
  | ROptional desc -> ROptional (map_desc_locs f desc)
  | RMaybe desc -> RMaybe (map_desc_locs f desc)
  | RRestArray desc -> RRestArray (map_desc_locs f desc)
  | RAbstract desc -> RAbstract (map_desc_locs f desc)
  | RTypeApp desc -> RTypeApp (map_desc_locs f desc)
  | RTypeAppImplicit desc -> RTypeAppImplicit (map_desc_locs f desc)
  | RThisTypeApp desc -> RThisTypeApp (map_desc_locs f desc)
  | RExtends desc -> RExtends (map_desc_locs f desc)
  | RClass desc -> RClass (map_desc_locs f desc)
  | RStatics desc -> RStatics (map_desc_locs f desc)
  | RSuperOf desc -> RSuperOf (map_desc_locs f desc)
  | RFrozen desc -> RFrozen (map_desc_locs f desc)
  | RBound desc -> RBound (map_desc_locs f desc)
  | RPredicateOf desc -> RPredicateOf (map_desc_locs f desc)
  | RPredicateCall desc -> RPredicateCall (map_desc_locs f desc)
  | RPredicateCallNeg desc -> RPredicateCallNeg (map_desc_locs f desc)
  | RRefined desc -> RRefined (map_desc_locs f desc)
  | RSpreadOf desc -> RSpreadOf (map_desc_locs f desc)
  | RShapeOf desc -> RShapeOf (map_desc_locs f desc)
  | RMatchingProp (s, desc) -> RMatchingProp (s, map_desc_locs f desc)
  | RTrusted desc -> RTrusted (map_desc_locs f desc)
  | RPrivate desc -> RPrivate (map_desc_locs f desc)
  | ( RObjectPatternRestProp | RArrayPatternRestProp | RCommonJSExports _ | RModule _
    | ROptionalChain | RReactProps | RReactElement _ | RReactClass | RReactComponent | RReactStatics
    | RReactDefaultProps | RReactState | RReactPropTypes | RReactChildren ) as r ->
    r
  | RReactChildrenOrType desc -> RReactChildrenOrType (map_desc_locs f desc)
  | RReactChildrenOrUndefinedOrType desc -> RReactChildrenOrUndefinedOrType (map_desc_locs f desc)
  | (RReactSFC | RReactConfig) as r -> r
  | RPossiblyMissingPropFromObj (propname, desc) ->
    RPossiblyMissingPropFromObj (propname, map_desc_locs f desc)
  | RWidenedObjProp desc -> RWidenedObjProp (map_desc_locs f desc)
  | RUnionBranching (desc, i) -> RUnionBranching (map_desc_locs f desc, i)

type 'loc virtual_reason = {
  test_id: int option;
  derivable: bool;
  desc: 'loc virtual_reason_desc;
  loc: 'loc;
  def_loc_opt: 'loc option;
  annot_loc_opt: 'loc option;
}
[@@deriving eq]

type reason = ALoc.t virtual_reason

type concrete_reason = Loc.t virtual_reason

type t = reason

let concretize_equal aloc_tables = equal_virtual_reason (ALoc.concretize_equal aloc_tables)

let in_range loc range =
  Loc.(
    let (line, line1, line2) = (loc.start.line, range.start.line, range._end.line) in
    (line1 < line || (line = line1 && range.start.column <= loc.start.column))
    && (line < line2 || (line = line2 && loc._end.column <= range._end.column)))

let string_of_source ?(strip_root = None) =
  File_key.(
    function
    | Builtins -> "(builtins)"
    | LibFile file ->
      begin
        match strip_root with
        | Some root ->
          let root_str = spf "%s%s" (Path.to_string root) Filename.dir_sep in
          if string_starts_with file root_str then
            spf "[LIB] %s" (Files.relative_path root_str file)
          else
            spf "[LIB] %s" (Filename.basename file)
        | None -> file
      end
    | SourceFile file
    | JsonFile file
    | ResourceFile file ->
      begin
        match strip_root with
        | Some root ->
          let root_str = spf "%s%s" (Path.to_string root) Filename.dir_sep in
          Files.relative_path root_str file
        | None -> file
      end)

let string_of_loc ?(strip_root = None) loc =
  Loc.(
    match loc.source with
    | None
    | Some File_key.Builtins ->
      ""
    | Some file -> spf "%s:%s" (string_of_source ~strip_root file) (Loc.to_string_no_source loc))

let string_of_aloc ?(strip_root = None) aloc =
  match ALoc.source aloc with
  | None
  | Some File_key.Builtins ->
    ""
  | Some file -> spf "%s:%s" (string_of_source ~strip_root file) (ALoc.to_string_no_source aloc)

let json_of_source ?(strip_root = None) =
  Hh_json.(
    function
    | Some x -> JSON_String (string_of_source ~strip_root x)
    | None -> JSON_Null)

let json_source_type_of_source =
  Hh_json.(
    function
    | Some (File_key.LibFile _) -> JSON_String "LibFile"
    | Some (File_key.SourceFile _) -> JSON_String "SourceFile"
    | Some (File_key.JsonFile _) -> JSON_String "JsonFile"
    | Some (File_key.ResourceFile _) -> JSON_String "ResourceFile"
    | Some File_key.Builtins -> JSON_String "Builtins"
    | None -> JSON_Null)

let json_of_loc_props ?(strip_root = None) ?(catch_offset_errors = false) ~offset_table loc =
  Hh_json.(
    Loc.(
      let offset_entry offset_table pos =
        let offset =
          try int_ (Offset_utils.offset offset_table pos)
          with Offset_utils.Offset_lookup_failed _ as exn ->
            if catch_offset_errors then
              JSON_Null
            else
              raise exn
        in
        [("offset", offset)]
      in
      let start =
        [
          ("line", int_ loc.start.line);
          (* It's not ideal that we use a different column numbering system here
           * versus other places (like the estree translator) *)
          ("column", int_ (loc.start.column + 1));
        ]
        @
        match offset_table with
        | None -> []
        | Some table -> offset_entry table loc.start
      in
      let end_ =
        [("line", int_ loc._end.line); ("column", int_ loc._end.column)]
        @
        match offset_table with
        | None -> []
        | Some table -> offset_entry table loc._end
      in
      [
        ("source", json_of_source ~strip_root loc.source);
        ("type", json_source_type_of_source loc.source);
        ("start", JSON_Object start);
        ("end", JSON_Object end_);
      ]))

let json_of_loc ?strip_root ?catch_offset_errors ~offset_table loc =
  Hh_json.(JSON_Object (json_of_loc_props ?strip_root ?catch_offset_errors ~offset_table loc))

(* reason constructors, accessors, etc. *)

let mk_reason_with_test_id test_id desc loc def_loc_opt annot_loc_opt =
  { test_id; derivable = false; desc; loc; def_loc_opt; annot_loc_opt }

let map_reason_locs f reason =
  let { def_loc_opt; annot_loc_opt; loc; desc; test_id; derivable } = reason in
  let loc' = f loc in
  let def_loc_opt' = Base.Option.map ~f def_loc_opt in
  let annot_loc_opt' = Base.Option.map ~f annot_loc_opt in
  let desc' = map_desc_locs f desc in
  {
    def_loc_opt = def_loc_opt';
    annot_loc_opt = annot_loc_opt';
    loc = loc';
    desc = desc';
    test_id;
    derivable;
  }

(* The current test_id is included in every new reason. *)
let mk_reason desc aloc = mk_reason_with_test_id (TestID.current ()) desc aloc None None

(* Lift a string to a reason. Usually used as a dummy reason. *)
let locationless_reason desc = mk_reason_with_test_id None desc ALoc.none None None

let func_reason ~async ~generator =
  let func_desc =
    match (async, generator) with
    | (true, true) -> RAsyncGenerator
    | (true, false) -> RAsync
    | (false, true) -> RGenerator
    | (false, false) -> RNormal
  in
  mk_reason (RFunction func_desc)

let poly_loc_of_reason r = r.loc

let aloc_of_reason = poly_loc_of_reason

let loc_of_reason = poly_loc_of_reason

(* TODO return ALoc *)
let def_poly_loc_of_reason r =
  match r.def_loc_opt with
  | Some loc -> loc
  | None -> aloc_of_reason r

let def_aloc_of_reason = def_poly_loc_of_reason

let def_loc_of_reason = def_poly_loc_of_reason

let annot_poly_loc_of_reason r = r.annot_loc_opt

let annot_aloc_of_reason = annot_poly_loc_of_reason

let annot_loc_of_reason = annot_poly_loc_of_reason

let function_desc_prefix = function
  | RAsync -> "async "
  | RGenerator -> "generator "
  | RAsyncGenerator -> "async generator "
  | RNormal -> ""
  | RUnknown -> "unknown "

let prettify_react_util s =
  let length = String.length s in
  if length < 6 then
    s
  else if String.sub s 0 6 = "React$" then
    "React." ^ String.sub s 6 (length - 6)
  else
    s

let rec string_of_desc = function
  | RTrusted r -> spf "trusted %s" (string_of_desc r)
  | RPrivate r -> spf "private %s" (string_of_desc r)
  | RNumber -> "number"
  | RBigInt -> "bigint"
  | RString
  | RLongStringLit _ ->
    "string"
  | RBoolean -> "boolean"
  | RMixed -> "mixed"
  | REmpty -> "empty"
  | RAnyImplicit -> "implicit 'any'"
  | RAnyExplicit -> "explicit 'any'"
  | RVoid -> "undefined"
  | RNull -> "null"
  | RVoidedNull -> "undefined (result of null short-circuiting an optional chain)"
  | RNullOrVoid -> "null or undefined"
  | RSymbol -> "symbol"
  | RExports -> "exports"
  | RStringLit "" -> "empty string"
  | RStringLit x -> spf "string literal `%s`" x
  | RNumberLit x -> spf "number literal `%s`" x
  | RBigIntLit x -> spf "bigint literal `%s`" x
  | RBooleanLit b -> spf "boolean literal `%s`" (string_of_bool b)
  | RMatchingProp (k, v) -> spf "object with property `%s` that matches %s" k (string_of_desc v)
  | RObject -> "object"
  | RObjectLit -> "object literal"
  | RObjectType -> "object type"
  | RObjectClassName -> "Object"
  | RInterfaceType -> "interface type"
  | RArray -> "array"
  | RArrayLit -> "array literal"
  | REmptyArrayLit -> "empty array literal"
  | RArrayType -> "array type"
  | RROArrayType -> "read-only array type"
  | RTupleType -> "tuple type"
  | RTupleElement -> "tuple element"
  | RTupleOutOfBoundsAccess -> "undefined (out of bounds tuple access)"
  | RTupleLength i -> spf "length `%d` (number) of tuple" i
  | RFunction func -> spf "%sfunction" (function_desc_prefix func)
  | RFunctionType -> "function type"
  | RFunctionBody -> "function body"
  | RFunctionCall d -> spf "call of %s" (string_of_desc d)
  | RFunctionCallType -> "`$Call`"
  | RFunctionUnusedArgument -> "unused function argument"
  | RJSXFunctionCall raw_jsx -> spf "`%s(...)`" raw_jsx
  | RJSXIdentifier (_, name) -> spf "`%s`" name
  | RJSXElement x ->
    (match x with
    | Some x -> spf "JSX element `%s`" x
    | None -> "JSX element")
  | RJSXElementProps _ -> "props"
  | RJSXText -> spf "JSX text"
  | RFbt -> "`<fbt/>`"
  | RUnaryOperator (operator, value) -> spf "%s %s" operator (string_of_desc value)
  | RBinaryOperator (operator, left, right) ->
    spf "%s %s %s" (string_of_desc left) operator (string_of_desc right)
  | RLogical (operator, left, right) ->
    spf "%s %s %s" (string_of_desc left) operator (string_of_desc right)
  | RTemplateString -> "template string"
  | RUnknownString -> "some string with unknown value"
  | RUnionEnum -> "literal union"
  | REnum name -> spf "enum `%s`" name
  | REnumRepresentation representation -> spf "%s enum" (string_of_desc representation)
  | RGetterSetterProperty -> "getter/setter property"
  | RThis -> "this"
  | RThisType -> "`this` type"
  | RExistential -> "existential"
  | RImplicitInstantiation -> "implicit instantiation"
  | RTooFewArgs -> "undefined (too few arguments)"
  | RTooFewArgsExpectedRest -> "undefined (too few arguments, expected default/rest parameters)"
  | RConstructorReturn -> "constructor return"
  | RNewObject -> "new object"
  | RUnion -> "union"
  | RUnionType -> "union type"
  | RIntersection -> "intersection"
  | RIntersectionType -> "intersection type"
  | RKeySet -> "key set"
  | RAnd -> "and"
  | RConditional -> "conditional"
  | RPrototype -> "prototype"
  | RObjectPrototype -> "object prototype"
  | RFunctionPrototype -> "function prototype"
  | RDestructuring -> "destructuring"
  | RDefaultValue -> "default value"
  | RConstructor -> "constructor"
  | RDefaultConstructor -> "default constructor"
  | RConstructorCall (RPolyType (RClass d)) -> string_of_desc d
  | RConstructorCall (RClass d) -> string_of_desc d
  | RConstructorCall d -> spf "new %s" (string_of_desc d)
  | RReturn -> "return"
  | RImplicitReturn desc -> spf "implicitly-returned %s" (string_of_desc desc)
  | RRegExp -> "regexp"
  | RSuper -> "super"
  | RNoSuper -> "empty super object"
  | RDummyPrototype -> "empty prototype object"
  | RDummyThis -> "bound `this` in method"
  | RTupleMap -> "`$TupleMap`"
  | RObjectMap -> "`$ObjMap`"
  | RObjectMapi -> "`$ObjMapi`"
  | RType x -> spf "`%s`" (prettify_react_util x)
  | RTypeAlias (x, _, _) -> spf "`%s`" (prettify_react_util x)
  | ROpaqueType x -> spf "`%s`" (prettify_react_util x)
  | RTypeParam (x, _, _) -> spf "`%s`" x
  | RTypeof x -> spf "`typeof %s`" x
  | RMethod (Some x) -> spf "method `%s`" x
  | RMethod None -> "computed method"
  | RIdentifier x -> spf "`%s`" (prettify_react_util x)
  | RIdentifierAssignment x -> spf "assignment of identifier `%s`" x
  | RMethodCall (Some x) -> spf "call of method `%s`" x
  | RMethodCall None -> "call of computed property"
  | RParameter (Some x) -> spf "`%s`" x
  | RParameter None -> "parameter"
  | RRestParameter (Some x) -> spf "rest parameter `%s`" x
  | RRestParameter None -> "rest parameter"
  | RProperty (Some x) -> spf "property `%s`" x
  | RProperty None -> "computed property"
  | RPrivateProperty x -> spf "property `#%s`" x
  | RMember { object_; property } -> spf "`%s%s`" object_ property
  | RPropertyAssignment (Some x) -> spf "assignment of property `%s`" x
  | RPropertyAssignment None -> "assignment of computed property/element"
  | RShadowProperty x -> spf ".%s" x
  | RPropertyOf (x, d) -> spf "property `%s` of %s" x (string_of_desc d)
  | RPropertyIsAString "" -> "empty string"
  | RPropertyIsAString x -> spf "string `%s`" x
  | RMissingProperty (Some x) -> spf "property `%s` does not exist" x
  | RMissingProperty None -> "computed property does not exist"
  | RUnknownProperty (Some x) -> spf "property `%s` of unknown type" x
  | RUnknownProperty None -> "computed property of unknown type"
  | RUndefinedProperty x -> spf "undefined property `%s`" x
  | RSomeProperty -> "some property"
  | RNameProperty d -> spf "property `name` of %s" (string_of_desc d)
  | RMissingAbstract d -> spf "undefined. Did you forget to declare %s?" (string_of_desc d)
  | RFieldInitializer x -> spf "field initializer for `%s`" x
  | RUntypedModule m -> spf "import from untyped module `%s`" m
  | RNamedImportedType (m, _) -> spf "Named import from module `%s`" m
  | RImportStarType n -> spf "import type * as %s" n
  | RImportStarTypeOf n -> spf "import typeof * as %s" n
  | RImportStar n -> spf "import * as %s" n
  | RCode x -> "`" ^ x ^ "`"
  | RDefaultImportedType (_, m) -> spf "Default import from `%s`" m
  | RAsyncImport -> "async import"
  | RCustom x -> x
  | RPolyType (RClass d) -> string_of_desc d
  | RPolyType d -> string_of_desc d
  | RPolyTest (_, d) -> string_of_desc d
  | RExactType d -> string_of_desc d
  | ROptional d -> spf "optional %s" (string_of_desc d)
  | RMaybe d ->
    let rec loop = function
      | RMaybe d -> loop d
      | d -> d
    in
    spf "nullable %s" (string_of_desc (loop d))
  | RRestArray _ -> "rest array"
  | RAbstract d -> spf "abstract %s" (string_of_desc d)
  | RTypeApp d -> string_of_desc d
  | RTypeAppImplicit d -> string_of_desc d
  | RThisTypeApp d -> spf "this instantiation of %s" (string_of_desc d)
  | RExtends d -> spf "extends %s" (string_of_desc d)
  | RClass d -> spf "class %s" (string_of_desc d)
  | RStatics d -> spf "statics of %s" (string_of_desc d)
  | RSuperOf d -> spf "super of %s" (string_of_desc d)
  | RFrozen d -> spf "frozen %s" (string_of_desc d)
  | RBound d -> spf "bound %s" (string_of_desc d)
  | RPredicateOf d -> spf "predicate encoded in %s" (string_of_desc d)
  | RPredicateCall d -> spf "predicate call to %s" (string_of_desc d)
  | RPredicateCallNeg d -> spf "negation of predicate call to %s" (string_of_desc d)
  | RRefined d -> spf "refined %s" (string_of_desc d)
  | RIncompatibleInstantiation x -> spf "`%s`" x
  | RSpreadOf d -> spf "spread of %s" (string_of_desc d)
  | RShapeOf d -> spf "%s" (string_of_desc d)
  | RObjectPatternRestProp -> "rest of object pattern"
  | RArrayPatternRestProp -> "rest of array pattern"
  | RCommonJSExports x -> spf "module `%s`" x
  | RModule x -> spf "module `%s`" x
  | ROptionalChain -> "optional chain"
  | RReactProps -> "props"
  | RReactElement x ->
    (match x with
    | Some x -> spf "`%s` element" x
    | None -> "React element")
  | RReactClass -> "React class"
  | RReactComponent -> "React component"
  | RReactStatics -> "statics of React class"
  | RReactDefaultProps -> "default props of React component"
  | RReactState -> "state of React component"
  | RReactPropTypes -> "propTypes of React component"
  | RReactChildren -> "children array"
  | RReactChildrenOrType desc -> spf "children array or %s" (string_of_desc desc)
  | RReactChildrenOrUndefinedOrType desc -> spf "children array or %s" (string_of_desc desc)
  | RReactSFC -> "React stateless functional component"
  | RReactConfig -> "config of React component"
  | RPossiblyMissingPropFromObj (propname, desc) ->
    spf "possibly missing property `%s` in %s" propname (string_of_desc desc)
  | RWidenedObjProp desc -> string_of_desc desc
  | RUnionBranching (desc, _) -> string_of_desc desc

let string_of_reason ?(strip_root = None) r =
  let spos = string_of_aloc ~strip_root (aloc_of_reason r) in
  let desc = string_of_desc r.desc in
  if spos = "" then
    desc
  else if desc = "" then
    spos
  else
    spf "%s: %s" spos desc

let dump_reason ?(strip_root = None) r =
  spf
    "%s: %S%s"
    (string_of_aloc ~strip_root (aloc_of_reason r))
    (string_of_desc r.desc)
    begin
      match r.test_id with
      | Some n -> spf " (test %d)" n
      | None -> ""
    end

let desc_of_reason =
  let rec loop = function
    | RTypeAlias (_, _, desc)
    | RUnionBranching (desc, _)
    | RPolyTest (_, desc) ->
      loop desc
    | desc -> desc
  in
  fun ?(unwrap = true) r ->
    if not unwrap then
      r.desc
    else
      loop r.desc

let internal_name name = spf ".%s" name

let is_internal_name name = String.length name >= 1 && name.[0] = '.'

let internal_module_name name = spf ".$module__%s" name

let is_internal_module_name name = string_starts_with name ".$module__"

let uninternal_module_name name =
  if is_internal_module_name name then
    String.sub name 10 (String.length name - 10)
  else
    name

(* Instantiable reasons identify tvars that are created for the purpose of
   instantiation: they are fresh rather than shared, and should become types
   that flow to them. We assume these characteristics when performing
   speculative matching (even though we don't yet enforce them). *)
let is_instantiable_reason r =
  match desc_of_reason r with
  | RTypeParam _
  | RThisType
  | RExistential ->
    true
  | RImplicitInstantiation -> true
  | _ -> false

(* TODO: Property accesses create unresolved tvars to hold results, even when
   the object(s) on which the property accesses happen may be resolved. This can
   and should be fixed, for various benefits including but not limited to more
   precise type inference. But meanwhile we need to consider results of property
   accesses that might result in sentinel property values as constants to decide
   membership in disjoint unions, instead of asking for unnecessary annotations
   to make progress. According to Facebook's style guide, constant properties
   should have names like CONSTANT_PROPERTY, so we bet that when properties with
   such names are accessed, their types have the 0->1 property.

   As an example, suppose that we have an object `Tags` that stores tags of a
   disjoint union, e.g. { ACTION_FOO: 'foo', ACTION_BAR: 'bar' }.

   Then the types of Tags.ACTION_FOO and Tags.ACTION_BAR are assumed to be 0->1.
*)
let is_constant_reason r =
  match desc_of_reason r with
  | RIdentifier x ->
    (* A single-letter variable name which happens to be upper-case should not
       be confused with a constant reason. This should really be further
       restricted to `const`-declared identifiers in scope. Or, better yet,
       removing this heuristic entirely. *)
    let len = String.length x in
    if len < 2 then
      false
    else
      is_not_lowercase x 0 (len - 1)
  | RProperty (Some x)
  | RPrivateProperty x
  | RMember { object_ = _; property = x }
  | RPropertyOf (x, _)
  | RPropertyIsAString x ->
    let len = String.length x in
    if len = 0 then
      false
    else
      is_not_lowercase x 0 (len - 1)
  | _ -> false

let is_typemap_reason r =
  match desc_of_reason r with
  | RTupleMap
  | RObjectMap
  | RObjectMapi ->
    true
  | _ -> false

let is_calltype_reason r =
  match desc_of_reason r with
  | RTupleMap
  | RObjectMap
  | RObjectMapi
  | RFunctionCallType ->
    true
  | _ -> false

let is_literal_object_reason r =
  match desc_of_reason r with
  | RObjectLit
  | RFrozen RObjectLit
  | RSpreadOf _
  | RObjectPatternRestProp
  | RFunction _
  | RStatics (RFunction _)
  | RReactProps
  | RReactElement _
  | RJSXElementProps _ ->
    true
  | _ -> false

let is_literal_array_reason r =
  match desc_of_reason r with
  | RArrayLit
  | REmptyArrayLit ->
    true
  | _ -> false

let is_derivable_reason r = r.derivable

let derivable_reason r = { r with derivable = true }

let builtin_reason desc =
  { Loc.none with Loc.source = Some File_key.Builtins }
  |> ALoc.of_loc
  |> mk_reason desc
  |> derivable_reason

let is_builtin_reason f r = r.loc |> f |> ( = ) (Some File_key.Builtins)

let is_lib_reason r =
  r.loc |> ALoc.source |> Base.Option.value_map ~default:false ~f:File_key.is_lib_file

let is_lib_reason_def r =
  def_aloc_of_reason r
  |> ALoc.source
  |> Base.Option.value_map ~default:false ~f:File_key.is_lib_file

let is_blamable_reason r = not (r.loc = ALoc.none || is_lib_reason r)

(* reason transformers: *)

(* returns reason with new description and position of original *)
let update_desc_reason f r =
  mk_reason_with_test_id
    r.test_id
    (f (desc_of_reason ~unwrap:false r))
    (poly_loc_of_reason r)
    r.def_loc_opt
    (annot_poly_loc_of_reason r)

let update_desc_new_reason f r =
  mk_reason_with_test_id
    r.test_id
    (f (desc_of_reason ~unwrap:false r))
    (poly_loc_of_reason r)
    None
    None

let replace_desc_reason desc r =
  mk_reason_with_test_id r.test_id desc r.loc r.def_loc_opt r.annot_loc_opt

let replace_desc_new_reason desc r = mk_reason_with_test_id r.test_id desc r.loc None None

(* returns reason with new location and description of original *)
let repos_reason loc reason =
  let def_aloc_opt =
    let def_loc = def_poly_loc_of_reason reason in
    if loc = def_loc then
      None
    else
      Some def_loc
  in
  mk_reason_with_test_id reason.test_id reason.desc loc def_aloc_opt reason.annot_loc_opt

let annot_reason ~annot_loc reason = { reason with annot_loc_opt = Some annot_loc }

let opt_annot_reason ?annot_loc reason =
  match annot_loc with
  | None -> reason
  | Some annot_loc -> annot_reason ~annot_loc reason

let mk_annot_reason desc annot_loc = annot_reason ~annot_loc (mk_reason desc annot_loc)

module ReasonMap = WrappedMap.Make (struct
  type t = reason

  let compare = Stdlib.compare
end)

(* Creates a description string for an arbitrary expression. This description
 * will be used to describe some code in error messages which are designed to be
 * human readable.
 *
 * We want to keep these descriptions *short* so we omit a lot of information in
 * places where expressions may often go recursive. For instance, object and
 * array literals are abbreviated as [...] and {...} respectively.
 *
 * The wrap argument provides a rough heuristic for when wrapping is necessary.
 * We set wrap to true when we need to append something to the final expression.
 * Then expressions which need to be wrapped will call do_wrap. e.g.
 *
 *     (1 + 2).p
 *     o.p
 *     o[1 + 2]
 *
 * In the first example we need to wrap 1 + 2 to correctly print the property
 * access. However, we don't need to wrap o in o.p. In o[1 + 2] we don't need to
 * wrap 1 + 2 since it is already wrapped in a sense. *)
let rec code_desc_of_expression ~wrap (_, x) =
  let do_wrap =
    if wrap then
      fun s ->
    "(" ^ s ^ ")"
    else
      fun s ->
    s
  in
  let open Ast.Expression in
  match x with
  | Array { Array.elements = []; _ } -> "[]"
  | Array _ -> "[...]"
  | ArrowFunction { Ast.Function.body = Ast.Function.BodyExpression ((_, Object _) as e); _ } ->
    do_wrap ("(...) => (" ^ code_desc_of_expression ~wrap:false e ^ ")")
  | ArrowFunction { Ast.Function.body = Ast.Function.BodyExpression e; _ } ->
    do_wrap ("(...) => " ^ code_desc_of_expression ~wrap:false e)
  | ArrowFunction _ -> do_wrap "(...) => { ... }"
  | Assignment { Assignment.left; operator; right; comments = _ } ->
    let left = code_desc_of_pattern left in
    let right = code_desc_of_expression ~wrap:false right in
    let operator =
      match operator with
      | None -> "="
      | Some op -> Flow_ast_utils.string_of_assignment_operator op
    in
    do_wrap (left ^ " " ^ operator ^ " " ^ right)
  | Binary { Binary.operator; left; right; comments = _ } ->
    do_wrap (code_desc_of_operation left (`Binary operator) right)
  | Call { Call.callee; targs; arguments; comments = _ } ->
    let targs =
      match targs with
      | None -> ""
      | Some (_, { CallTypeArgs.arguments = []; comments = _ }) -> "<>"
      | Some (_, { CallTypeArgs.arguments = _ :: _; comments = _ }) -> "<...>"
    in
    let args =
      match arguments with
      | (_loc, { ArgList.arguments = []; comments = _ }) -> "()"
      | (_loc, { ArgList.arguments = _ :: _; comments = _ }) -> "(...)"
    in
    code_desc_of_expression ~wrap:true callee ^ targs ^ args
  | Class _ -> "class { ... }"
  | Conditional { Conditional.test; consequent; alternate; comments = _ } ->
    let wrap_test =
      match test with
      | (_, Conditional _) -> true
      | _ -> false
    in
    do_wrap
      ( code_desc_of_expression ~wrap:wrap_test test
      ^ " ? "
      ^ code_desc_of_expression ~wrap:false consequent
      ^ " : "
      ^ code_desc_of_expression ~wrap:false alternate )
  | Function _ -> "function () { ... }"
  | Identifier (_, { Ast.Identifier.name = x; comments = _ }) -> x
  | Import { Import.argument; comments = _ } ->
    "import(" ^ code_desc_of_expression ~wrap:false argument ^ ")"
  | JSXElement x -> code_desc_of_jsx_element x
  | JSXFragment _ -> "<>...</>"
  | Ast.Expression.Literal x -> code_desc_of_literal x
  | Logical { Logical.operator; left; right; comments = _ } ->
    do_wrap (code_desc_of_operation left (`Logical operator) right)
  | Member { Member._object; property; comments = _ } ->
    let o = code_desc_of_expression ~wrap:true _object in
    let p = code_desc_of_property ~optional:false property in
    o ^ p
  | MetaProperty
      {
        MetaProperty.meta = (_, { Ast.Identifier.name = o; comments = _ });
        property = (_, { Ast.Identifier.name = p; comments = _ });
        comments = _;
      } ->
    o ^ "." ^ p
  | New { New.callee; targs; arguments; comments = _ } ->
    let targs =
      match targs with
      | None -> ""
      | Some (_, { CallTypeArgs.arguments = []; comments = _ }) -> "<>"
      | Some (_, { CallTypeArgs.arguments = _ :: _; comments = _ }) -> "<...>"
    in
    let args =
      match arguments with
      | None -> ""
      | Some (_loc, { ArgList.arguments = []; comments = _ }) -> "()"
      | Some (_loc, { ArgList.arguments = _ :: _; comments = _ }) -> "(...)"
    in
    do_wrap ("new " ^ code_desc_of_expression ~wrap:true callee ^ targs ^ args)
  | Object _ -> "{...}"
  | OptionalCall { OptionalCall.call = { Call.callee; targs; arguments; comments = _ }; optional }
    ->
    let targ_string =
      match targs with
      | None -> ""
      | Some (_, { CallTypeArgs.arguments = []; comments = _ }) -> "<>"
      | Some (_, { CallTypeArgs.arguments = _ :: _; comments = _ }) -> "<...>"
    in
    let arg_string =
      match arguments with
      | (_loc, { ArgList.arguments = []; comments = _ }) -> "()"
      | (_loc, { ArgList.arguments = _; comments = _ }) -> "(...)"
    in
    code_desc_of_expression ~wrap:true callee
    ^ ( if optional then
        "?."
      else
        "" )
    ^ targ_string
    ^ arg_string
  | OptionalMember { OptionalMember.member = { Member._object; property; comments = _ }; optional }
    ->
    let o = code_desc_of_expression ~wrap:true _object in
    let p = code_desc_of_property ~optional property in
    o ^ p
  | Sequence { Sequence.expressions; comments = _ } ->
    code_desc_of_expression ~wrap (List.hd (List.rev expressions))
  | Super _ -> "super"
  | TaggedTemplate { TaggedTemplate.tag; _ } -> code_desc_of_expression ~wrap:true tag ^ "`...`"
  | TemplateLiteral _ -> "`...`"
  | This _ -> "this"
  | TypeCast { TypeCast.expression; _ } -> code_desc_of_expression ~wrap expression
  | Unary { Unary.operator; argument; comments = _ } ->
    let x = code_desc_of_expression ~wrap:true argument in
    let op =
      Unary.(
        match operator with
        | Minus -> "-"
        | Plus -> "+"
        | Not -> "!"
        | BitNot -> "~"
        | Typeof -> "typeof "
        | Void -> "void "
        | Delete -> "delete "
        | Await -> "await ")
    in
    do_wrap (op ^ x)
  | Update { Update.operator; prefix; argument; comments = _ } ->
    let x = code_desc_of_expression ~wrap:true argument in
    let op =
      Update.(
        match operator with
        | Increment -> "++"
        | Decrement -> "--")
    in
    do_wrap
      ( if prefix then
        op ^ x
      else
        x ^ op )
  | Yield { Yield.argument = Some x; delegate = false; _ } ->
    do_wrap ("yield " ^ code_desc_of_expression ~wrap:false x)
  | Yield { Yield.argument = Some x; delegate = true; _ } ->
    do_wrap ("yield* " ^ code_desc_of_expression ~wrap:false x)
  | Yield { Yield.argument = None; delegate = false; _ } -> "yield"
  | Yield { Yield.argument = None; delegate = true; _ } -> "yield*"
  (* TODO *)
  | Comprehension _
  | Generator _ ->
    do_wrap "..."

and code_desc_of_pattern (_, x) =
  let open Ast.Pattern in
  match x with
  | Object _ -> "{...}"
  | Array _ -> "[...]"
  | Identifier { Identifier.name = (_, { Ast.Identifier.name; comments = _ }); _ } -> name
  | Expression x -> code_desc_of_expression ~wrap:false x

(* Implementation of operator flattening logic lifted from Prettier:
 * https://github.com/prettier/prettier/blob/dd78f31aaf5b4522b780f13194d57308e5fdf53b/src/common/util.js#L328-L399 *)
and code_desc_of_operation =
  let open Ast.Expression in
  let string_of_operator = function
    | `Binary op -> Flow_ast_utils.string_of_binary_operator op
    | `Logical op ->
      (match op with
      | Logical.Or -> "||"
      | Logical.And -> "&&"
      | Logical.NullishCoalesce -> "??")
  in
  let should_flatten =
    Binary.(
      let precedence = function
        | `Logical Logical.Or -> 0
        | `Logical Logical.NullishCoalesce -> 0
        | `Logical Logical.And -> 1
        | `Binary BitOr -> 2
        | `Binary Xor -> 3
        | `Binary BitAnd -> 4
        | `Binary (Equal | NotEqual | StrictEqual | StrictNotEqual) -> 5
        | `Binary (LessThan | LessThanEqual | GreaterThan | GreaterThanEqual | In | Instanceof) -> 6
        | `Binary (LShift | RShift | RShift3) -> 7
        | `Binary (Plus | Minus) -> 8
        | `Binary (Mult | Div | Mod) -> 9
        | `Binary Exp -> 10
      in
      let equality = function
        | `Binary (Equal | NotEqual | StrictEqual | StrictNotEqual) -> true
        | _ -> false
      in
      let multiplicative = function
        | `Binary (Mult | Div | Mod) -> true
        | _ -> false
      in
      let bitshift = function
        | `Binary (LShift | RShift | RShift3) -> true
        | _ -> false
      in
      fun a b ->
        if precedence a <> precedence b then
          false
        else if a = `Binary Exp then
          false
        else if equality a && equality b then
          false
        else if (a = `Binary Mod && multiplicative b) || (b = `Binary Mod && multiplicative a) then
          false
        else if bitshift a && bitshift b then
          false
        else
          true)
  in
  fun left op right ->
    let wrap_left =
      match left with
      | (_, Binary { Binary.operator; _ }) -> not (should_flatten op (`Binary operator))
      | (_, Logical { Logical.operator; _ }) -> not (should_flatten op (`Logical operator))
      | _ -> true
    in
    let left = code_desc_of_expression ~wrap:wrap_left left in
    let right = code_desc_of_expression ~wrap:true right in
    let op = string_of_operator op in
    left ^ " " ^ op ^ " " ^ right

and code_desc_of_jsx_element x =
  let open Ast.JSX in
  match (snd x.openingElement).Opening.name with
  | Identifier (_, { Identifier.name; comments = _ }) -> "<" ^ name ^ " />"
  | NamespacedName
      ( _,
        {
          NamespacedName.namespace = (_, { Identifier.name = a; comments = __POS_OF__ });
          name = (_, { Identifier.name = b; comments = _ });
        } ) ->
    "<" ^ a ^ ":" ^ b ^ " />"
  | MemberExpression x ->
    let rec loop = function
      | ( _,
          {
            MemberExpression._object =
              MemberExpression.Identifier (_, { Identifier.name = a; comments = _ });
            property = (_, { Identifier.name = b; comments = _ });
          } ) ->
        a ^ "." ^ b
      | ( _,
          {
            MemberExpression._object = MemberExpression.MemberExpression a;
            property = (_, { Identifier.name = b; comments = _ });
          } ) ->
        loop a ^ "." ^ b
    in
    "<" ^ loop x ^ " />"

and code_desc_of_literal x =
  let open Ast in
  match x.Literal.value with
  | Literal.String x when String.length x > 16 -> "'" ^ String.sub x 0 10 ^ "...'"
  | _ -> x.Literal.raw

and code_desc_of_property ~optional property =
  match property with
  | Ast.Expression.Member.PropertyIdentifier (_, { Ast.Identifier.name = x; comments = _ }) ->
    ( if optional then
      "?."
    else
      "." )
    ^ x
  | Ast.Expression.Member.PropertyPrivateName
      (_, { Ast.PrivateName.id = (_, { Ast.Identifier.name = x; comments = _ }); comments = _ }) ->
    ( if optional then
      "?.#"
    else
      ".#" )
    ^ x
  | Ast.Expression.Member.PropertyExpression x ->
    ( if optional then
      "?.["
    else
      "[" )
    ^ code_desc_of_expression ~wrap:false x
    ^ "]"

let rec mk_expression_reason =
  let open Ast.Expression in
  function
  | (loc, TypeCast { TypeCast.expression; _ }) -> repos_reason loc (mk_expression_reason expression)
  | (loc, Object _) -> mk_reason RObjectLit loc
  | (loc, Array _) -> mk_reason RArrayLit loc
  | (loc, ArrowFunction { Ast.Function.async; _ }) -> func_reason ~async ~generator:false loc
  | (loc, Function { Ast.Function.async; generator; _ }) -> func_reason ~async ~generator loc
  | (loc, Ast.Expression.Literal { Ast.Literal.value = Ast.Literal.String ""; _ }) ->
    mk_reason (RStringLit "") loc
  | (loc, TaggedTemplate _) -> mk_reason RTemplateString loc
  | (loc, TemplateLiteral _) -> mk_reason RTemplateString loc
  | (loc, Member { Member._object; property; comments = _ }) ->
    mk_reason
      (RMember
         {
           object_ = code_desc_of_expression ~wrap:true _object;
           property = code_desc_of_property ~optional:false property;
         })
      loc
  | (loc, _) as x -> mk_reason (RCode (code_desc_of_expression ~wrap:false x)) loc

let mk_pattern_reason ((loc, _) as patt) = mk_reason (RCode (code_desc_of_pattern patt)) loc

(* TODO: replace RCustom descriptions with proper descriptions *)
let unknown_elem_empty_array_desc = RCustom "unknown element type of empty array"

let inferred_union_elem_array_desc =
  RCustom
    "inferred union of array element types (alternatively, provide an annotation to summarize the array element type)"

(* Classifies a reason description. These classifications can be used to
 * implement various asthetic behaviors in error messages when we would like to
 * distinguish between different error "classes".
 *
 * The classifications we currently support:
 *
 * - `Scalar: The type *cannot* recursively hold any other types. For example,
 *   number is a scalar but an object like {p: number} is not.
 * - `Nullish: The type is null or undefined. Nullish types are also `Scalar.
 * - `Array: The type is an array. This depends on Flow's custom implementation
 *   of arrays and tuples.
 * - `Unclassified: Everything else which hasn't been classified yet.
 *)
let classification_of_reason r =
  match desc_of_reason ~unwrap:true r with
  | RNumber
  | RBigInt
  | RString
  | RSymbol
  | RBoolean
  | RLongStringLit _
  | RStringLit _
  | RNumberLit _
  | RBigIntLit _
  | RBooleanLit _
  | RJSXText
  | RFbt
  | RTemplateString
  | RUnknownString
  | RUnionEnum
  | RKeySet
  | RRegExp ->
    `Scalar
  | RVoid
  | RNull
  | RVoidedNull
  | RNullOrVoid ->
    `Nullish
  | RArray
  | RArrayLit
  | REmptyArrayLit
  | RArrayType
  | RROArrayType
  | RTupleType
  | RRestArray _
  | RArrayPatternRestProp ->
    `Array
  | RMixed
  | REmpty
  | RAnyExplicit
  | RAnyImplicit
  | RMatchingProp _
  | RObject
  | RObjectLit
  | RObjectType
  | RObjectClassName
  | RInterfaceType
  | RTupleElement
  | RTupleLength _
  | RTupleOutOfBoundsAccess
  | RFunction _
  | RFunctionType
  | RFunctionBody
  | RFunctionCall _
  | RFunctionCallType
  | RFunctionUnusedArgument
  | RJSXFunctionCall _
  | RJSXIdentifier _
  | RJSXElementProps _
  | RJSXElement _
  | RUnaryOperator _
  | RBinaryOperator _
  | RLogical _
  | RGetterSetterProperty
  | RThis
  | RThisType
  | RExistential
  | RImplicitInstantiation
  | RTooFewArgs
  | RTooFewArgsExpectedRest
  | RConstructorReturn
  | RNewObject
  | RUnion
  | RUnionType
  | RIntersection
  | RIntersectionType
  | RAnd
  | RConditional
  | RPrototype
  | RObjectPrototype
  | RFunctionPrototype
  | RDestructuring
  | RDefaultValue
  | RConstructor
  | RDefaultConstructor
  | RConstructorCall _
  | RReturn
  | RImplicitReturn _
  | RSuper
  | RNoSuper
  | RDummyPrototype
  | RDummyThis
  | RTupleMap
  | RObjectMap
  | RObjectMapi
  | RType _
  | RTypeAlias _
  | ROpaqueType _
  | RTypeParam _
  | RTypeof _
  | RMethod _
  | RMethodCall _
  | RParameter _
  | RRestParameter _
  | RIdentifier _
  | RIdentifierAssignment _
  | RPropertyAssignment _
  | RProperty _
  | RPrivateProperty _
  | RShadowProperty _
  | RMember _
  | RPropertyOf _
  | RPropertyIsAString _
  | RMissingProperty _
  | RUnknownProperty _
  | RUndefinedProperty _
  | RSomeProperty
  | RNameProperty _
  | RMissingAbstract _
  | RFieldInitializer _
  | RUntypedModule _
  | RNamedImportedType _
  | RImportStarType _
  | RImportStarTypeOf _
  | RImportStar _
  | RDefaultImportedType _
  | RAsyncImport
  | RCode _
  | RCustom _
  | RExports
  | RPolyType _
  | RPolyTest _
  | RExactType _
  | ROptional _
  | RMaybe _
  | RAbstract _
  | RTypeApp _
  | RTypeAppImplicit _
  | RThisTypeApp _
  | RExtends _
  | RClass _
  | RStatics _
  | RSuperOf _
  | RFrozen _
  | RBound _
  | RPredicateOf _
  | RPredicateCall _
  | RPredicateCallNeg _
  | RRefined _
  | RIncompatibleInstantiation _
  | RSpreadOf _
  | RShapeOf _
  | RObjectPatternRestProp
  | RCommonJSExports _
  | RModule _
  | ROptionalChain
  | RReactProps
  | RReactElement _
  | RReactClass
  | RReactComponent
  | RReactStatics
  | RReactDefaultProps
  | RReactState
  | RReactPropTypes
  | RReactChildren
  | RReactChildrenOrType _
  | RReactChildrenOrUndefinedOrType _
  | RReactSFC
  | RReactConfig
  | RPossiblyMissingPropFromObj _
  | RWidenedObjProp _
  | RUnionBranching _
  | RTrusted _
  | RPrivate _
  | REnum _
  | REnumRepresentation _ ->
    `Unclassified

let is_nullish_reason r = classification_of_reason r = `Nullish

let is_scalar_reason r =
  let c = classification_of_reason r in
  c = `Scalar || c = `Nullish

let is_array_reason r = classification_of_reason r = `Array

let invalidate_rtype_alias = function
  | RTypeAlias (name, Some _, desc) -> RTypeAlias (name, None, desc)
  | desc -> desc