2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
14 module Inf
= Typing_inference_env
15 module Cls
= Decl_provider.Class
17 (*****************************************************************************)
18 (* Profiling utilities *)
19 (*****************************************************************************)
22 List.fold ~init
:0.0 ~f
:( +. ) samples
/. Float.of_int
(List.length samples
)
24 let standard_deviation mean samples
=
29 let diff = sample
-. mean in
30 (diff *. diff) +. acc
)
33 let sosqm = sosq /. Float.of_int
(List.length samples
) in
36 (*****************************************************************************)
37 (* Types, constants *)
38 (*****************************************************************************)
41 | Ifc
of string * string
46 | Glean_index
of string
53 | Get_some_file_deps
of int
54 | Identify_symbol
of int * int
55 | Find_local
of int * int
56 | Get_member
of string
63 | RewriteGlobalInference
64 | Find_refs
of int * int
65 | Highlight_refs
of int * int
68 | Go_to_impl
of int * int
70 | Hover
of (int * int) option
72 | Shape_analysis
of string
73 | Refactor_sound_dynamic
of string * string * string
74 | RemoveDeadUnsafeCasts
76 | SDT_analysis
of string
80 extra_builtins
: string list
;
82 error_format
: Errors.format
;
84 max_errors
: int option;
85 tcopt
: GlobalOptions.t
;
87 out_extension
: string;
89 should_print_position
: bool;
90 custom_hhi_path
: string option;
91 profile_type_check_multi
: int option;
92 memtrace
: string option;
93 pessimise_builtins
: bool;
94 rust_provider_backend
: bool;
97 (** If the user passed --root, then all pathnames have to be canonicalized.
98 The fact of whether they passed --root is kind of stored inside Relative_path
99 global variables: the Relative_path.(path_of_prefix Root) is either "/"
100 if they failed to pass something, or the thing that they passed. *)
101 let use_canonical_filenames () =
102 not
(String.equal
"/" (Relative_path.path_of_prefix
Relative_path.Root
))
104 (* Canonical builtins from our hhi library *)
105 let hhi_builtins = Hhi.get_raw_hhi_contents
()
107 (* All of the stuff that hh_single_type_check relies on is sadly not contained
108 * in the hhi library, so we include a very small number of magic builtins *)
111 ( "hh_single_type_check_magic.hhi",
114 ^
"function hh_show<T>(<<__AcceptDisposable>> readonly T $val)[]:T {}\n"
115 ^
"function hh_expect<T>(<<__AcceptDisposable>> readonly T $val)[]:T {}\n"
116 ^
"function hh_expect_equivalent<T>(<<__AcceptDisposable>> readonly T $val)[]:T {}\n"
117 ^
"function hh_show_env()[]:void {}\n"
118 ^
"function hh_log_level(string $key, int $level)[]:void {}\n"
119 ^
"function hh_force_solve()[]:void {}"
120 ^
"function hh_time(string $command, string $tag = '_'):void {}\n"
124 let pessimised_magic_builtins =
126 ( "hh_single_type_check_magic.hhi",
129 ^
"function hh_show<T as supportdyn<mixed>>(<<__AcceptDisposable>> readonly ~T $val)[]:~T {}\n"
130 ^
"function hh_expect<T as supportdyn<mixed>>(<<__AcceptDisposable>> readonly ~T $val)[]:~T {}\n"
131 ^
"function hh_expect_equivalent<T as supportdyn<mixed>>(<<__AcceptDisposable>> readonly ~T $val)[]:~T {}\n"
132 ^
"function hh_show_env()[]:void {}\n"
133 ^
"function hh_log_level(string $key, int $level)[]:void {}\n"
134 ^
"function hh_force_solve()[]:void {}"
138 (*****************************************************************************)
140 (*****************************************************************************)
144 Out_channel.output_string
oc str
;
145 Out_channel.close
oc;
148 let print_error format ?
(oc = stderr
) l
=
151 | Errors.Context
-> (fun e
-> Contextual_error_formatter.to_string e
)
152 | Errors.Raw
-> (fun e
-> Raw_error_formatter.to_string e
)
153 | Errors.Plain
-> (fun e
-> Errors.to_string e
)
154 | Errors.Highlighted
-> Highlighted_error_formatter.to_string
156 let absolute_errors = User_error.to_absolute l
in
157 Out_channel.output_string
oc (formatter absolute_errors)
159 let write_error_list format errors
oc max_errors
=
160 let (shown_errors
, dropped_errors
) =
161 match max_errors
with
162 | Some max_errors
-> List.split_n errors max_errors
163 | None
-> (errors
, [])
165 if not
(List.is_empty errors
) then (
166 List.iter ~f
:(print_error format ~
oc) shown_errors
;
168 Errors.format_summary
171 (List.length dropped_errors
)
174 | Some summary
-> Out_channel.output_string
oc summary
177 Out_channel.output_string
oc "No errors\n";
180 let print_error_list format errors max_errors
=
181 let (shown_errors
, dropped_errors
) =
182 match max_errors
with
183 | Some max_errors
-> List.split_n errors max_errors
184 | None
-> (errors
, [])
186 if not
(List.is_empty errors
) then (
187 List.iter ~f
:(print_error format
) shown_errors
;
189 Errors.format_summary
192 (List.length dropped_errors
)
195 | Some summary
-> Out_channel.output_string stderr summary
198 Printf.printf
"No errors\n"
200 let print_errors format
(errors
: Errors.t
) max_errors
: unit =
201 print_error_list format
(Errors.get_sorted_error_list errors
) max_errors
203 let print_errors_if_present (errors
: Errors.error list
) =
204 if not
(List.is_empty errors
) then (
205 let errors_output = Errors.convert_errors_to_string errors
in
206 Printf.printf
"Errors:\n";
207 List.iter
errors_output ~f
:(fun err_output
->
208 Printf.printf
" %s\n" err_output
)
211 let comma_string_to_iset (s
: string) : ISet.t
=
212 Str.split
(Str.regexp
", *") s
|> List.map ~f
:int_of_string
|> ISet.of_list
214 let parse_options () =
215 let fn_ref = ref [] in
216 let extra_builtins = ref [] in
217 let usage = Printf.sprintf
"Usage: %s filename\n" Sys.argv
.(0) in
218 let mode = ref Errors
in
219 let no_builtins = ref false in
221 let log_key = ref "" in
222 let log_levels = ref SMap.empty
in
223 let max_errors = ref None
in
224 let batch_mode = ref false in
227 | Errors
-> mode := x
228 | _
-> raise
(Arg.Bad
"only a single mode should be specified")
230 let ifc_mode = ref "" in
231 let set_ifc lattice
=
232 set_mode (Ifc
(!ifc_mode, lattice
)) ();
235 let error_format = ref Errors.Highlighted
in
236 let forbid_nullable_cast = ref false in
237 let deregister_attributes = ref None
in
238 let auto_namespace_map = ref None
in
239 let log_inference_constraints = ref None
in
240 let timeout = ref None
in
241 let disallow_byref_dynamic_calls = ref (Some
false) in
242 let disallow_byref_calls = ref (Some
false) in
243 let set_bool x
() = x
:= Some
true in
244 let set_bool_ x
() = x
:= true in
245 let set_float_ x f
= x
:= f
in
246 let rust_provider_backend = ref false in
247 let skip_hierarchy_checks = ref false in
248 let skip_tast_checks = ref false in
249 let out_extension = ref ".out" in
250 let like_type_hints = ref false in
251 let union_intersection_type_hints = ref false in
252 let call_coeffects = ref true in
253 let local_coeffects = ref true in
254 let strict_contexts = ref true in
255 let like_casts = ref false in
256 let simple_pessimize = ref 0.0 in
257 let symbolindex_file = ref None
in
258 let check_xhp_attribute = ref false in
259 let check_redundant_generics = ref false in
260 let disallow_static_memoized = ref false in
261 let enable_supportdyn_hint = ref false in
262 let enable_class_level_where_clauses = ref false in
263 let disable_legacy_soft_typehints = ref false in
264 let allow_new_attribute_syntax = ref false in
265 let allow_toplevel_requires = ref false in
266 let global_inference = ref false in
267 let ordered_solving = ref false in
268 let reinfer_types = ref [] in
269 let const_static_props = ref false in
270 let disable_legacy_attribute_syntax = ref false in
271 let const_attribute = ref false in
272 let const_default_func_args = ref false in
273 let const_default_lambda_args = ref false in
274 let disallow_silence = ref false in
275 let abstract_static_props = ref false in
276 let glean_service = ref (GleanOptions.service
GlobalOptions.default
) in
277 let glean_hostname = ref (GleanOptions.hostname
GlobalOptions.default
) in
278 let glean_port = ref (GleanOptions.port
GlobalOptions.default
) in
279 let glean_reponame = ref (GleanOptions.reponame
GlobalOptions.default
) in
280 let disallow_func_ptrs_in_constants = ref false in
281 let error_php_lambdas = ref false in
282 let disallow_discarded_nullable_awaitables = ref false in
283 let disable_xhp_element_mangling = ref false in
284 let disable_xhp_children_declarations = ref false in
285 let enable_xhp_class_modifier = ref false in
286 let verbosity = ref 0 in
287 let disable_hh_ignore_error = ref 0 in
288 let is_systemlib = ref false in
289 let enable_higher_kinded_types = ref false in
290 let allowed_fixme_codes_strict = ref None
in
291 let allowed_decl_fixme_codes = ref None
in
292 let method_call_inference = ref false in
293 let report_pos_from_reason = ref false in
294 let enable_sound_dynamic = ref false in
295 let always_pessimise_return = ref false in
296 let consider_type_const_enforceable = ref false in
297 let disallow_fun_and_cls_meth_pseudo_funcs = ref false in
298 let disallow_inst_meth = ref false in
299 let disable_enum_classes = ref false in
300 let interpret_soft_types_as_like_types = ref false in
301 let enable_strict_string_concat_interp = ref false in
302 let ignore_unsafe_cast = ref false in
303 let math_new_code = ref false in
304 let typeconst_concrete_concrete_error = ref false in
305 let enable_strict_const_semantics = ref 0 in
306 let strict_wellformedness = ref 0 in
307 let meth_caller_only_public_visibility = ref true in
308 let require_extends_implements_ancestors = ref false in
309 let strict_value_equality = ref false in
310 let expression_tree_virtualize_functions = ref false in
311 let naming_table = ref None
in
312 let root = ref None
in
313 let sharedmem_config = ref SharedMem.default_config
in
314 let print_position = ref true in
315 let enforce_sealed_subclasses = ref false in
316 let everything_sdt = ref false in
317 let pessimise_builtins = ref false in
318 let custom_hhi_path = ref None
in
319 let explicit_consistent_constructors = ref 0 in
320 let require_types_class_consts = ref 0 in
321 let type_printer_fuel =
322 ref (TypecheckerOptions.type_printer_fuel GlobalOptions.default
)
324 let profile_type_check_multi = ref None
in
325 let profile_top_level_definitions =
326 ref (TypecheckerOptions.profile_top_level_definitions GlobalOptions.default
)
328 let memtrace = ref None
in
329 let enable_global_access_check_files = ref [] in
330 let enable_global_access_check_functions = ref SSet.empty
in
331 let global_access_check_on_write = ref true in
332 let global_access_check_on_read = ref true in
333 let refactor_mode = ref "" in
334 let refactor_analysis_mode = ref "" in
335 let set_enable_global_access_check_functions s
=
336 let json_obj = Hh_json.json_of_file s
in
339 | Hh_json.JSON_String str
->
340 enable_global_access_check_functions :=
341 SSet.add str
!enable_global_access_check_functions
345 | Hh_json.JSON_Array lst
-> List.iter lst ~f
:add_function
346 | _
-> enable_global_access_check_functions := SSet.empty
348 let allow_all_files_for_module_declarations = ref true in
349 let loop_iteration_upper_bound = ref None
in
350 let substitution_mutation = ref false in
351 let allow_all_locations_for_type_constant_in_enum_class = ref true in
352 let tast_under_dynamic = ref false in
355 ( "--no-print-position",
356 Arg.Unit
(fun _
-> print_position := false),
357 " Don't print positions while printing TASTs and NASTs" );
359 Arg.String
(fun s
-> naming_table := Some s
),
360 " Naming table, to look up undefined symbols; needs --root" );
362 Arg.String
(fun s
-> root := Some s
),
363 " Root for where to look up undefined symbols; needs --naming-table" );
365 Arg.String
(fun f
-> extra_builtins := f
:: !extra_builtins),
366 " HHI file to parse and declare" );
368 Arg.Tuple
[Arg.String
(fun m
-> ifc_mode := m
); Arg.String
set_ifc],
369 " Run the flow analysis" );
370 ( "--shape-analysis",
374 set_mode (Shape_analysis
mode) ()),
375 " Run the flow analysis" );
376 ( "--refactor-sound-dynamic",
379 Arg.String
(fun mode -> refactor_analysis_mode := mode);
380 Arg.String
(fun mode -> refactor_mode := mode);
385 (Refactor_sound_dynamic
386 (!refactor_analysis_mode, !refactor_mode, x
))
389 " Run the flow analysis" );
390 ( "--deregister-attributes",
391 Arg.Unit
(set_bool deregister_attributes),
392 " Ignore all functions with attribute '__PHPStdLib'" );
393 ( "--auto-namespace-map",
396 auto_namespace_map :=
397 Some
(ServerConfig.convert_auto_namespace_to_map m
)),
398 " Alias namespaces" );
399 ( "--no-call-coeffects",
400 Arg.Unit
(fun () -> call_coeffects := false),
401 " Turns off call coeffects" );
402 ( "--no-local-coeffects",
403 Arg.Unit
(fun () -> local_coeffects := false),
404 " Turns off local coeffects" );
405 ( "--no-strict-contexts",
406 Arg.Unit
(fun () -> strict_contexts := false),
407 " Do not enforce contexts to be defined within Contexts namespace" );
408 ("--colour", Arg.Unit
(set_mode Color
), " Produce colour output");
409 ("--color", Arg.Unit
(set_mode Color
), " Produce color output");
410 ("--coverage", Arg.Unit
(set_mode Coverage
), " Produce coverage output");
412 Arg.Unit
(set_mode Cst_search
),
413 " Search the concrete syntax tree of the given file using the pattern"
415 ^
" (The pattern is a JSON object adhering to the search DSL.)" );
416 ( "--dump-symbol-info",
417 Arg.Unit
(set_mode Dump_symbol_info
),
418 " Dump all symbol information" );
420 Arg.String
(fun output_dir
-> set_mode (Glean_index output_dir
) ()),
421 " Run indexer and output json in provided dir" );
426 | "raw" -> error_format := Errors.Raw
427 | "context" -> error_format := Errors.Context
428 | "highlighted" -> error_format := Errors.Highlighted
429 | "plain" -> error_format := Errors.Plain
430 | _
-> print_string
"Warning: unrecognized error format.\n"),
431 "<raw|context|highlighted|plain> Error formatting style; (default: highlighted)"
433 ("--lint", Arg.Unit
(set_mode Lint
), " Produce lint errors");
434 ("--lint-json", Arg.Unit
(set_mode Lint_json
), " Produce json lint output");
437 " Don't use builtins (e.g. ConstSet); implied by --root" );
439 Arg.String
(fun s
-> out_extension := s
),
440 " output file extension (default .out)" );
441 ("--dump-deps", Arg.Unit
(set_mode Dump_deps
), " Print dependencies");
442 ( "--dump-dep-hashes",
443 Arg.Unit
(set_mode Dump_dep_hashes
),
444 " Print dependency hashes" );
445 ( "--dump-glean-deps",
446 Arg.Unit
(set_mode Dump_glean_deps
),
447 " Print dependencies in the Glean format" );
448 ( "--dump-inheritance",
449 Arg.Unit
(set_mode Dump_inheritance
),
450 " Print inheritance" );
451 ( "--get-some-file-deps",
452 Arg.Int
(fun depth
-> set_mode (Get_some_file_deps depth
) ()),
453 " Print a list of files this file depends on. The provided integer is the depth of the traversal. Requires --root, --naming-table and --depth"
455 ( "--identify-symbol",
458 Arg.Int
(fun x
-> line := x
);
460 (fun column
-> set_mode (Identify_symbol
(!line, column
)) ());
462 "<pos> Show info about symbol at given line and column" );
466 Arg.Int
(fun x
-> line := x
);
467 Arg.Int
(fun column
-> set_mode (Find_local
(!line, column
)) ());
469 "<pos> Find all usages of local at given line and column" );
471 Arg.Int
(fun num_errors
-> max_errors := Some num_errors
),
472 " Maximum number of errors to display" );
473 ("--outline", Arg.Unit
(set_mode Outline
), " Print file outline");
474 ("--nast", Arg.Unit
(set_mode Dump_nast
), " Print out the named AST");
475 ("--tast", Arg.Unit
(set_mode Dump_tast
), " Print out the typed AST");
477 Arg.Unit
(set_mode Type
),
478 " Extract types from the typed AST and print one per line as JSON" );
479 ("--tast-check", Arg.Unit
(set_mode Check_tast
), " Typecheck the tast");
481 Arg.Unit
(set_mode Dump_stripped_tast
),
482 " Print out the typed AST, stripped of type information."
483 ^
" This can be compared against the named AST to look for holes." );
485 Arg.Unit
(set_mode RewriteGlobalInference
),
486 " Rewrite the file after inferring types using global inference"
487 ^
" (requires --global-inference)." );
488 ( "--global-inference",
489 Arg.Set
global_inference,
490 " Global type inference to infer missing type annotations." );
491 ( "--ordered-solving",
492 Arg.Set
ordered_solving,
493 " Optimized solver for type variables. Experimental." );
495 Arg.String
(fun s
-> reinfer_types := Str.split
(Str.regexp
", *") s
),
496 " List of type hint to be ignored and inferred again using global inference."
501 Arg.Int
(fun x
-> line := x
);
502 Arg.Int
(fun column
-> set_mode (Find_refs
(!line, column
)) ());
504 "<pos> Find all usages of a symbol at given line and column" );
508 Arg.Int
(fun x
-> line := x
);
509 Arg.Int
(fun column
-> set_mode (Go_to_impl
(!line, column
)) ());
511 "<pos> Find all implementations of a symbol at given line and column" );
512 ( "--highlight-refs",
515 Arg.Int
(fun x
-> line := x
);
516 Arg.Int
(fun column
-> set_mode (Highlight_refs
(!line, column
)) ());
518 "<pos> Highlight all usages of a symbol at given line and column" );
520 Arg.Unit
(set_mode Decl_compare
),
521 " Test comparison functions used in incremental mode on declarations"
522 ^
" in provided file" );
523 ( "--shallow-class-diff",
524 Arg.Unit
(set_mode Shallow_class_diff
),
525 " Test shallow class comparison used in incremental mode on shallow class declarations"
527 ( "--forbid_nullable_cast",
528 Arg.Set
forbid_nullable_cast,
529 " Forbid casting from nullable values." );
532 (fun class_and_member_id
->
533 set_mode (Get_member class_and_member_id
) ()),
534 " Given ClassName::MemberName, fetch the decl of members with that name and print them."
536 ( "--log-inference-constraints",
537 Arg.Unit
(set_bool log_inference_constraints),
538 " Log inference constraints to Scuba." );
540 Arg.Int
(fun secs
-> timeout := Some secs
),
541 " Timeout in seconds for checking a function or a class." );
545 Arg.String
(fun x
-> log_key := x
);
547 (fun level
-> log_levels := SMap.add
!log_key level
!log_levels);
549 " Set the log level for a key" );
552 " Typecheck each file passed in independently" );
553 ( "--disallow-static-memoized",
554 Arg.Set
disallow_static_memoized,
555 " Disallow static memoized methods on non-final methods" );
556 ( "--check-xhp-attribute",
557 Arg.Set
check_xhp_attribute,
558 " Typechecks xhp required attributes" );
559 ( "--disallow-byref-dynamic-calls",
560 Arg.Unit
(set_bool disallow_byref_dynamic_calls),
561 " Disallow passing arguments by reference to dynamically called functions [e.g. $foo(&$bar)]"
563 ( "--disallow-byref-calls",
564 Arg.Unit
(set_bool disallow_byref_calls),
565 " Disallow passing arguments by reference in any form [e.g. foo(&$bar)]"
567 ( "--rust-provider-backend",
568 Arg.Set
rust_provider_backend,
569 " Use the Rust implementation of Provider_backend (including decl-folding)"
571 ( "--skip-hierarchy-checks",
572 Arg.Set
skip_hierarchy_checks,
573 " Do not apply checks on class hierarchy (override, implements, etc)" );
574 ( "--skip-tast-checks",
575 Arg.Set
skip_tast_checks,
576 " Do not apply checks using TAST visitors" );
577 ( "--union-intersection-type-hints",
578 Arg.Set
union_intersection_type_hints,
579 " Allows union and intersection types to be written in type hint positions"
581 ( "--like-type-hints",
582 Arg.Set
like_type_hints,
583 " Allows like types to be written in type hint positions" );
586 " Allows like types to be written in as expressions" );
587 ( "--simple-pessimize",
588 Arg.Set_float
simple_pessimize,
589 " At coercion points, if a type is not enforceable, wrap it in like. Float argument 0.0 to 1.0 sets frequency"
591 ( "--like-types-all",
594 set_bool_ like_type_hints ();
595 set_bool_ like_casts ();
596 set_float_ simple_pessimize 1.0),
597 " Enables all like types features" );
598 ( "--naive-implicit-pess",
601 set_bool_ enable_sound_dynamic ();
602 set_bool_ everything_sdt ();
603 set_bool_ like_type_hints ();
604 set_bool_ always_pessimise_return ();
605 set_bool_ consider_type_const_enforceable ();
606 set_bool_ enable_supportdyn_hint ();
607 set_bool_ pessimise_builtins ()),
608 " Enables naive implicit pessimisation" );
612 set_bool_ enable_sound_dynamic ();
613 set_bool_ everything_sdt ();
614 set_bool_ like_type_hints ();
615 set_bool_ enable_supportdyn_hint ();
616 set_bool_ pessimise_builtins ()),
617 " Enables implicit pessimisation" );
621 set_bool_ enable_sound_dynamic ();
622 set_bool_ like_type_hints ();
623 set_bool_ enable_supportdyn_hint ();
624 set_bool_ pessimise_builtins ();
625 custom_hhi_path := Some dir
),
626 " Enables checking explicitly pessimised files. Requires path to pessimised .hhi files "
628 ( "--symbolindex-file",
629 Arg.String
(fun str
-> symbolindex_file := Some str
),
630 " Load the symbol index from this file" );
631 ( "--enable-supportdyn-hint",
632 Arg.Set
enable_supportdyn_hint,
633 " Allow the supportdyn type hint" );
634 ( "--enable-class-level-where-clauses",
635 Arg.Set
enable_class_level_where_clauses,
636 " Enables support for class-level where clauses" );
637 ( "--disable-legacy-soft-typehints",
638 Arg.Set
disable_legacy_soft_typehints,
639 " Disables the legacy @ syntax for soft typehints (use __Soft instead)"
641 ( "--allow-new-attribute-syntax",
642 Arg.Set
allow_new_attribute_syntax,
643 " Allow the new @ attribute syntax (disables legacy soft typehints)" );
644 ( "--allow-toplevel-requires",
645 Arg.Set
allow_toplevel_requires,
646 " Allow `require()` and similar at the top-level" );
647 ( "--const-static-props",
648 Arg.Set
const_static_props,
649 " Enable static properties to be const" );
650 ( "--disable-legacy-attribute-syntax",
651 Arg.Set
disable_legacy_attribute_syntax,
652 " Disable the legacy <<...>> user attribute syntax" );
653 ("--const-attribute", Arg.Set
const_attribute, " Allow __Const attribute");
654 ( "--const-default-func-args",
655 Arg.Set
const_default_func_args,
656 " Statically check default function arguments are constant initializers"
658 ( "--const-default-lambda-args",
659 Arg.Set
const_default_lambda_args,
660 " Statically check default lambda args are constant."
661 ^
" Produces a subset of errors of const-default-func-args" );
662 ( "--disallow-silence",
663 Arg.Set
disallow_silence,
664 " Disallow the error suppression operator, @" );
665 ( "--abstract-static-props",
666 Arg.Set
abstract_static_props,
667 " Static properties can be abstract" );
669 Arg.String
(fun str
-> glean_service := str
),
670 " glean service name" );
671 ( "--glean-hostname",
672 Arg.String
(fun str
-> glean_hostname := str
),
674 ("--glean-port", Arg.Int
(fun x
-> glean_port := x
), " glean port number");
675 ( "--glean-reponame",
676 Arg.String
(fun str
-> glean_reponame := str
),
677 " glean repo name" );
678 ( "--disallow-func-ptrs-in-constants",
679 Arg.Set
disallow_func_ptrs_in_constants,
680 " Disallow use of HH\\fun and HH\\class_meth in constants and constant initializers"
682 ( "--disallow-php-lambdas",
683 Arg.Set
error_php_lambdas,
684 " Disallow php style anonymous functions." );
685 ( "--disallow-discarded-nullable-awaitables",
686 Arg.Set
disallow_discarded_nullable_awaitables,
687 " Error on using discarded nullable awaitables" );
688 ( "--disable-xhp-element-mangling",
689 Arg.Set
disable_xhp_element_mangling,
690 " Disable mangling of XHP elements :foo. That is, :foo:bar is now \\foo\\bar, not xhp_foo__bar"
692 ( "--disable-xhp-children-declarations",
693 Arg.Set
disable_xhp_children_declarations,
694 " Disable XHP children declarations, e.g. children (foo, bar+)" );
695 ( "--enable-xhp-class-modifier",
696 Arg.Set
enable_xhp_class_modifier,
697 " Enable the XHP class modifier, xhp class name {} will define an xhp class."
700 Arg.Int
(fun v
-> verbosity := v
),
701 " Verbosity as an integer." );
702 ( "--disable-hh-ignore-error",
703 Arg.Int
(( := ) disable_hh_ignore_error),
704 " Forbid HH_IGNORE_ERROR comments as an alternative to HH_FIXME, or treat them as normal comments."
707 Arg.Set
is_systemlib,
708 " Enable systemlib annotations and other internal-only features" );
709 ( "--enable-higher-kinded-types",
710 Arg.Set
enable_higher_kinded_types,
711 " Enable support for higher-kinded types" );
712 ( "--allowed-fixme-codes-strict",
714 (fun s
-> allowed_fixme_codes_strict := Some
(comma_string_to_iset s
)),
715 " List of fixmes that are allowed in strict mode." );
716 ( "--allowed-decl-fixme-codes",
718 (fun s
-> allowed_decl_fixme_codes := Some
(comma_string_to_iset s
)),
719 " List of fixmes that are allowed in declarations." );
720 ( "--method-call-inference",
721 Arg.Set
method_call_inference,
722 " Infer constraints for method calls. NB: incompatible with like types."
724 ( "--report-pos-from-reason",
725 Arg.Set
report_pos_from_reason,
726 " Flag errors whose position is derived from reason information in types."
728 ( "--enable-sound-dynamic-type",
729 Arg.Set
enable_sound_dynamic,
730 " Enforce sound dynamic types. Experimental." );
731 ( "--always-pessimise-return",
732 Arg.Set
always_pessimise_return,
733 " Consider all return types unenforceable." );
734 ( "--consider-type-const-enforceable",
735 Arg.Set
consider_type_const_enforceable,
736 " Consider type constants to potentially be enforceable." );
737 ( "--disallow-fun-and-cls-meth-pseudo-funcs",
738 Arg.Set
disallow_fun_and_cls_meth_pseudo_funcs,
739 " Disable parsing of fun() and class_meth()." );
740 ( "--disallow-inst-meth",
741 Arg.Set
disallow_inst_meth,
742 " Disable parsing of inst_meth()." );
743 ( "--disable-enum-classes",
744 Arg.Set
disable_enum_classes,
745 " Disable the enum classes extension." );
746 ( "--interpret-soft-types-as-like-types",
747 Arg.Set
interpret_soft_types_as_like_types,
748 " Types declared with <<__Soft>> (runtime logs but doesn't throw) become like types."
750 ( "--enable-strict-string-concat-interp",
751 Arg.Set
enable_strict_string_concat_interp,
752 " Require arguments are arraykey types in string concatenation and interpolation."
754 ( "--ignore-unsafe-cast",
755 Arg.Set
ignore_unsafe_cast,
756 " Ignore unsafe_cast and retain the original type of the expression" );
758 Arg.Set
math_new_code,
759 " Use a new error code for math operations: addition, subtraction, division, multiplication, exponentiation"
761 ( "--typeconst-concrete-concrete-error",
762 Arg.Set
typeconst_concrete_concrete_error,
763 " Raise an error when a concrete type constant is overridden by a concrete type constant in a child class."
765 ( "--enable-strict-const-semantics",
766 Arg.Int
(fun x
-> enable_strict_const_semantics := x
),
767 " Raise an error when a concrete constants is overridden or multiply defined"
769 ( "--strict-wellformedness",
770 Arg.Int
(fun x
-> strict_wellformedness := x
),
771 " Re-introduce missing well-formedness checks in AST positions" );
772 ( "--meth-caller-only-public-visibility",
773 Arg.Bool
(fun x
-> meth_caller_only_public_visibility := x
),
774 " Controls whether meth_caller can be used on non-public methods" );
778 Arg.Int
(fun x
-> line := x
);
779 Arg.Int
(fun column
-> set_mode (Hover
(Some
(!line, column
))) ());
781 "<pos> Display hover tooltip" );
782 ( "--hover-at-caret",
783 Arg.Unit
(fun () -> set_mode (Hover None
) ()),
784 " Show the hover information indicated by // ^ hover-at-caret" );
786 Arg.Unit
(fun () -> set_mode Apply_quickfixes
()),
787 " Apply quickfixes for all the errors in the file, and print the resulting code."
789 ( "--require-extends-implements-ancestors",
790 Arg.Set
require_extends_implements_ancestors,
791 " Consider `require extends` and `require implements` as ancestors when checking a class"
793 ( "--strict-value-equality",
794 Arg.Set
strict_value_equality,
795 " Emit an error when \"==\" or \"!=\" is used to compare values that are incompatible types."
797 ( "--enable-sealed-subclasses",
798 Arg.Set
enforce_sealed_subclasses,
799 " Require all __Sealed arguments to be subclasses" );
800 ( "--everything-sdt",
801 Arg.Set
everything_sdt,
802 " Treat all classes, functions, and traits as though they are annotated with <<__SupportDynamicType>>, unless they are annotated with <<__NoAutoDynamic>>"
804 ( "--pessimise-builtins",
805 Arg.Set
pessimise_builtins,
806 " Treat built-in collections and Hack arrays as though they contain ~T"
808 ( "--custom-hhi-path",
809 Arg.String
(fun s
-> custom_hhi_path := Some s
),
810 " Use custom hhis" );
811 ( "--explicit-consistent-constructors",
812 Arg.Int
(( := ) explicit_consistent_constructors),
813 " Raise an error for <<__ConsistentConstruct>> without an explicit constructor; 1 for traits, 2 for all "
815 ( "--require-types-class-consts",
816 Arg.Int
(( := ) require_types_class_consts),
817 " Raise an error for class constants missing types; 1 for abstract constants, 2 for all "
819 ( "--profile-type-check-twice",
820 Arg.Unit
(fun () -> profile_type_check_multi := Some
1),
821 " Typecheck the file twice" );
822 ( "--profile-type-check-multi",
823 Arg.Int
(fun n
-> profile_type_check_multi := Some n
),
824 " Typecheck the files n times extra (!)" );
825 ( "--profile-top-level-definitions",
826 Arg.Set
profile_top_level_definitions,
827 " Profile typechecking of top-level definitions" );
829 Arg.String
(fun s
-> memtrace := Some s
),
830 " Write memtrace to this file (typical extension .ctf)" );
831 ( "--type-printer-fuel",
832 Arg.Int
(( := ) type_printer_fuel),
833 " Sets the amount of fuel that the type printer can use to display an individual type. Default: "
835 (TypecheckerOptions.type_printer_fuel GlobalOptions.default
) );
836 ( "--enable-global-access-check-files",
839 enable_global_access_check_files := String_utils.split '
,' s
),
840 " Run global access checker on any file whose path is prefixed by the argument (use \"\\\" for hh_single_type_check)"
842 ( "--enable-global-access-check-functions",
843 Arg.String
set_enable_global_access_check_functions,
844 " Run global access checker on functions listed in the given JSON file"
846 ( "--disable-global-access-check-on-write",
847 Arg.Clear
global_access_check_on_write,
848 " Disable global access checker to check global writes" );
849 ( "--disable-global-access-check-on-read",
850 Arg.Clear
global_access_check_on_read,
851 " Disable global access checker to check global reads" );
852 ( "--overwrite-loop-iteration-upper-bound",
853 Arg.Int
(fun u
-> loop_iteration_upper_bound := Some u
),
854 " Sets the maximum number of iterations that will be used to typecheck loops"
856 ( "--expression-tree-virtualize-functions",
857 Arg.Set
expression_tree_virtualize_functions,
858 " Enables function virtualization in Expression Trees" );
859 ( "--substitution-mutation",
860 Arg.Set
substitution_mutation,
861 " Applies substitution mutation to applicable entities and typechecks them"
863 ( "--remove-dead-unsafe-casts",
864 Arg.Unit
(fun () -> set_mode RemoveDeadUnsafeCasts
()),
865 " Removes dead unsafe casts from a file" );
866 ( "--count-imprecise-types",
867 Arg.Unit
(fun () -> set_mode CountImpreciseTypes
()),
868 " Counts the number of mixed, dynamic, and nonnull types in a file" );
869 ( "--tast-under-dynamic",
870 Arg.Set
tast_under_dynamic,
871 " Produce variations of definitions as they are checked under dynamic assumptions"
877 set_mode (SDT_analysis command
) ()),
878 " Analyses to support Sound Dynamic rollout" );
882 (* Sanity check that all option descriptions are well-formed. *)
883 List.iter
options ~f
:(fun (_
, _
, description
) ->
885 String.is_prefix description ~prefix
:" "
886 || String.is_prefix description ~prefix
:"<"
892 "Descriptions should start with <foo> or a leading space, got: %S"
895 let options = Arg.align ~limit
:25 options in
896 Arg.parse
options (fun fn
-> fn_ref := fn
:: !fn_ref) usage;
898 match (!fn_ref, !mode) with
899 | ([], (Get_member _
| Type
)) -> []
900 | ([], _
) -> die usage
910 | Get_some_file_deps _
->
911 if Option.is_none
!naming_table then
912 raise
(Arg.Bad
"--get-some-file-deps requires --naming-table");
913 if Option.is_none
!root then
914 raise
(Arg.Bad
"--get-some-file-deps requires --root")
917 if Option.is_some
!naming_table && Option.is_none
!root then
918 failwith
"--naming-table needs --root";
920 (* --root implies certain things... *)
923 | None
-> Path.make
"/" (* if none specified, we use this dummy *)
925 if Option.is_none
!naming_table then
926 failwith
"--root needs --naming-table";
927 (* builtins are already provided by project at --root, so we shouldn't provide our own *)
929 (* Following will throw an exception if .hhconfig not found *)
930 let (_config_hash
, config
) =
931 Config_file.parse_hhconfig
932 (Filename.concat
root Config_file.file_path_relative_to_repo_root
)
934 (* We will pick up values from .hhconfig, unless they've been overridden at the command-line. *)
935 if Option.is_none
!auto_namespace_map then
936 auto_namespace_map :=
938 |> Config_file.Getters.string_opt
"auto_namespace_map"
939 |> Option.map ~f
:ServerConfig.convert_auto_namespace_to_map
;
940 if Option.is_none
!allowed_fixme_codes_strict then
941 allowed_fixme_codes_strict :=
943 |> Config_file.Getters.string_opt
"allowed_fixme_codes_strict"
944 |> Option.map ~f
:comma_string_to_iset;
946 ServerConfig.make_sharedmem_config
948 (ServerArgs.default_options ~
root)
949 ServerLocalConfig.default
;
950 (* Path.make canonicalizes it, i.e. resolves symlinks *)
954 let tcopt : GlobalOptions.t
=
956 ~tco_saved_state_loading
:GlobalOptions.default_saved_state_loading
957 ?po_deregister_php_stdlib
:!deregister_attributes
958 ?tco_log_inference_constraints
:!log_inference_constraints
959 ?tco_timeout
:!timeout
960 ?po_auto_namespace_map
:!auto_namespace_map
961 ?tco_disallow_byref_dynamic_calls
:!disallow_byref_dynamic_calls
962 ?tco_disallow_byref_calls
:!disallow_byref_calls
963 ~
allowed_fixme_codes_strict:
964 (Option.value !allowed_fixme_codes_strict ~default
:ISet.empty
)
965 ~tco_check_xhp_attribute
:!check_xhp_attribute
966 ~tco_check_redundant_generics
:!check_redundant_generics
967 ~tco_skip_hierarchy_checks
:!skip_hierarchy_checks
968 ~tco_skip_tast_checks
:!skip_tast_checks
969 ~tco_like_type_hints
:!like_type_hints
970 ~tco_union_intersection_type_hints
:!union_intersection_type_hints
971 ~tco_strict_contexts
:!strict_contexts
972 ~tco_coeffects
:!call_coeffects
973 ~tco_coeffects_local
:!local_coeffects
974 ~tco_like_casts
:!like_casts
975 ~tco_simple_pessimize
:!simple_pessimize
976 ~
log_levels:!log_levels
977 ~po_enable_class_level_where_clauses
:!enable_class_level_where_clauses
978 ~po_disable_legacy_soft_typehints
:!disable_legacy_soft_typehints
979 ~po_allow_new_attribute_syntax
:!allow_new_attribute_syntax
980 ~po_disallow_toplevel_requires
:(not
!allow_toplevel_requires)
981 ~tco_const_static_props
:!const_static_props
982 ~tco_global_inference
:!global_inference
983 ~tco_ordered_solving
:!ordered_solving
984 ~tco_gi_reinfer_types
:!reinfer_types
985 ~po_disable_legacy_attribute_syntax
:!disable_legacy_attribute_syntax
986 ~tco_const_attribute
:!const_attribute
987 ~po_const_default_func_args
:!const_default_func_args
988 ~po_const_default_lambda_args
:!const_default_lambda_args
989 ~po_disallow_silence
:!disallow_silence
990 ~po_abstract_static_props
:!abstract_static_props
991 ~po_disallow_func_ptrs_in_constants
:!disallow_func_ptrs_in_constants
992 ~tco_check_attribute_locations
:true
993 ~tco_error_php_lambdas
:!error_php_lambdas
994 ~tco_disallow_discarded_nullable_awaitables
:
995 !disallow_discarded_nullable_awaitables
996 ~
glean_service:!glean_service
997 ~
glean_hostname:!glean_hostname
998 ~
glean_port:!glean_port
999 ~
glean_reponame:!glean_reponame
1000 ~po_disable_xhp_element_mangling
:!disable_xhp_element_mangling
1001 ~po_disable_xhp_children_declarations
:!disable_xhp_children_declarations
1002 ~po_enable_xhp_class_modifier
:!enable_xhp_class_modifier
1003 ~po_disable_hh_ignore_error
:!disable_hh_ignore_error
1004 ~tco_is_systemlib
:!is_systemlib
1005 ~tco_higher_kinded_types
:!enable_higher_kinded_types
1006 ~po_allowed_decl_fixme_codes
:
1007 (Option.value !allowed_decl_fixme_codes ~default
:ISet.empty
)
1008 ~po_allow_unstable_features
:true
1009 ~tco_method_call_inference
:!method_call_inference
1010 ~tco_report_pos_from_reason
:!report_pos_from_reason
1011 ~tco_enable_sound_dynamic
:!enable_sound_dynamic
1012 ~po_disallow_fun_and_cls_meth_pseudo_funcs
:
1013 !disallow_fun_and_cls_meth_pseudo_funcs
1014 ~po_disallow_inst_meth
:!disallow_inst_meth
1016 (if is_ifc_mode then
1020 ~tco_global_access_check_files_enabled
:!enable_global_access_check_files
1021 ~tco_global_access_check_functions_enabled
:
1022 !enable_global_access_check_functions
1023 ~tco_global_access_check_on_write
:!global_access_check_on_write
1024 ~tco_global_access_check_on_read
:!global_access_check_on_read
1025 ~po_enable_enum_classes
:(not
!disable_enum_classes)
1026 ~po_interpret_soft_types_as_like_types
:!interpret_soft_types_as_like_types
1027 ~tco_enable_strict_string_concat_interp
:
1028 !enable_strict_string_concat_interp
1029 ~tco_ignore_unsafe_cast
:!ignore_unsafe_cast
1030 ~tco_math_new_code
:!math_new_code
1031 ~tco_typeconst_concrete_concrete_error
:!typeconst_concrete_concrete_error
1032 ~tco_enable_strict_const_semantics
:!enable_strict_const_semantics
1033 ~tco_strict_wellformedness
:!strict_wellformedness
1034 ~tco_meth_caller_only_public_visibility
:
1035 !meth_caller_only_public_visibility
1036 ~tco_require_extends_implements_ancestors
:
1037 !require_extends_implements_ancestors
1038 ~tco_strict_value_equality
:!strict_value_equality
1039 ~tco_enforce_sealed_subclasses
:!enforce_sealed_subclasses
1040 ~tco_everything_sdt
:!everything_sdt
1041 ~tco_pessimise_builtins
:!pessimise_builtins
1042 ~tco_explicit_consistent_constructors
:!explicit_consistent_constructors
1043 ~tco_require_types_class_consts
:!require_types_class_consts
1044 ~tco_type_printer_fuel
:!type_printer_fuel
1045 ~tco_profile_top_level_definitions
:!profile_top_level_definitions
1046 ~tco_allow_all_files_for_module_declarations
:
1047 !allow_all_files_for_module_declarations
1048 ~tco_loop_iteration_upper_bound
:!loop_iteration_upper_bound
1049 ~tco_expression_tree_virtualize_functions
:
1050 !expression_tree_virtualize_functions
1051 ~tco_substitution_mutation
:!substitution_mutation
1052 ~tco_allow_all_locations_for_type_constant_in_enum_class
:
1053 !allow_all_locations_for_type_constant_in_enum_class
1054 ~tco_tast_under_dynamic
:!tast_under_dynamic
1057 Errors.allowed_fixme_codes_strict :=
1058 GlobalOptions.allowed_fixme_codes_strict tcopt;
1059 Errors.report_pos_from_reason :=
1060 TypecheckerOptions.report_pos_from_reason tcopt;
1062 let tco_experimental_features =
1063 tcopt.GlobalOptions.tco_experimental_features
1065 let tco_experimental_features =
1066 if !forbid_nullable_cast then
1068 TypecheckerOptions.experimental_forbid_nullable_cast
1069 tco_experimental_features
1071 tco_experimental_features
1073 let tco_experimental_features =
1076 TypecheckerOptions.experimental_infer_flows
1077 tco_experimental_features
1079 tco_experimental_features
1081 let tco_experimental_features =
1082 if !disallow_static_memoized then
1084 TypecheckerOptions.experimental_disallow_static_memoized
1085 tco_experimental_features
1087 tco_experimental_features
1089 let tco_experimental_features =
1090 if !enable_supportdyn_hint then
1092 TypecheckerOptions.experimental_supportdynamic_type_hint
1093 tco_experimental_features
1095 tco_experimental_features
1097 let tco_experimental_features =
1098 if !always_pessimise_return then
1100 TypecheckerOptions.experimental_always_pessimise_return
1101 tco_experimental_features
1103 tco_experimental_features
1105 let tco_experimental_features =
1106 if !consider_type_const_enforceable then
1108 TypecheckerOptions.experimental_consider_type_const_enforceable
1109 tco_experimental_features
1111 tco_experimental_features
1114 let tcopt = { tcopt with GlobalOptions.tco_experimental_features } in
1117 extra_builtins = !extra_builtins;
1119 no_builtins = !no_builtins;
1120 max_errors = !max_errors;
1121 error_format = !error_format;
1123 batch_mode = !batch_mode;
1124 out_extension = !out_extension;
1125 verbosity = !verbosity;
1126 should_print_position
= !print_position;
1127 custom_hhi_path = !custom_hhi_path;
1128 profile_type_check_multi = !profile_type_check_multi;
1129 memtrace = !memtrace;
1130 pessimise_builtins = !pessimise_builtins;
1131 rust_provider_backend = !rust_provider_backend;
1135 if !rust_provider_backend then
1138 !sharedmem_config with
1139 shm_use_sharded_hashtbl
= true;
1141 max
!sharedmem_config.shm_cache_size
(2 * 1024 * 1024 * 1024);
1146 (* Make readable test output *)
1147 let replace_color input
=
1150 | (Some Unchecked
, str
) -> "<unchecked>" ^ str ^
"</unchecked>"
1151 | (Some Checked
, str
) -> "<checked>" ^ str ^
"</checked>"
1152 | (Some Partial
, str
) -> "<partial>" ^ str ^
"</partial>"
1153 | (None
, str
) -> str
)
1155 let print_colored fn type_acc
=
1156 let content = cat
(Relative_path.to_absolute fn
) in
1157 let results = ColorFile.go
content type_acc
in
1158 if Unix.isatty
Unix.stdout
then
1159 Tty.cprint
(ClientColorFile.replace_colors
results)
1161 print_string
(List.map ~f
:replace_color results |> String.concat ~sep
:"")
1163 let print_coverage type_acc
=
1164 ClientCoverageMetric.go ~json
:false (Some
(Coverage_level_defs.Leaf type_acc
))
1166 let print_global_inference_envs ctx ~
verbosity gienvs
=
1168 Typing_global_inference.StateSubConstraintGraphs.global_tvenvs
gienvs
1170 let tco_global_inference =
1171 TypecheckerOptions.global_inference (Provider_context.get_tcopt ctx
)
1173 if verbosity >= 2 && tco_global_inference then
1174 let should_log (pos
, gienv
) =
1176 match verbosity with
1178 when Filename.check_suffix
1179 (Relative_path.suffix
(Pos.filename pos
))
1184 file_relevant && (not
@@ List.is_empty
@@ Inf.get_vars_g gienv
)
1186 let env = Typing_env_types.empty ctx
Relative_path.default ~droot
:None
in
1188 List.filter
gienvs ~f
:should_log
1189 |> List.iter ~f
:(fun (pos
, gienv
) ->
1190 Typing_log.log_global_inference_env pos
env gienv
)
1192 let merge_global_inference_envs_opt ctx
gienvs :
1193 Typing_global_inference.StateConstraintGraph.t
option =
1194 if TypecheckerOptions.global_inference (Provider_context.get_tcopt ctx
) then
1195 let open Typing_global_inference
in
1196 let (type_map
, env, state_errors
) =
1197 StateConstraintGraph.merge_subgraphs ctx
[gienvs]
1199 (* we are not going to print type variables without any bounds *)
1200 let env = { env with inference_env
= Inf.compress
env.inference_env
} in
1201 Some
(type_map
, env, state_errors
)
1205 let print_global_inference_env
1206 env ~step_name state_errors
error_format max_errors =
1207 let print_header s
=
1209 print_endline
(String.map s ~f
:(const '
='
));
1211 print_endline
(String.map s ~f
:(const '
='
))
1213 print_header (Printf.sprintf
"%sd environment" step_name
);
1214 Typing_log.hh_show_full_env
Pos.none
env;
1216 print_header (Printf.sprintf
"%s errors" step_name
);
1218 (Typing_global_inference.StateErrors.elements state_errors
)
1219 ~f
:(fun (var
, errl
) ->
1220 Printf.fprintf stderr
"#%d\n" var
;
1221 print_error_list error_format errl
max_errors);
1222 Out_channel.flush stderr
1224 let print_merged_global_inference_env
1226 (gienv
: Typing_global_inference.StateConstraintGraph.t
option)
1229 if verbosity >= 1 then
1232 | Some
(_type_map
, gienv
, state_errors
) ->
1233 print_global_inference_env
1240 let print_solved_global_inference_env
1242 (gienv
: Typing_global_inference.StateSolvedGraph.t
option)
1245 if verbosity >= 1 then
1248 | Some
(gienv
, state_errors
, _type_map
) ->
1249 print_global_inference_env
1256 let solve_global_inference_env
1257 (gienv
: Typing_global_inference.StateConstraintGraph.t
) :
1258 Typing_global_inference.StateSolvedGraph.t
=
1259 Typing_global_inference.StateSolvedGraph.from_constraint_graph gienv
1261 let global_inference_merge_and_solve
1262 ~
verbosity ?
(error_format = Errors.Plain
) ?
max_errors ctx
gienvs =
1263 print_global_inference_envs ctx ~
verbosity gienvs;
1264 let gienv = merge_global_inference_envs_opt ctx
gienvs in
1265 print_merged_global_inference_env ~
verbosity gienv error_format max_errors;
1266 let gienv = Option.map
gienv ~f
:solve_global_inference_env in
1267 print_solved_global_inference_env ~
verbosity gienv error_format max_errors;
1270 let print_elapsed fn desc ~start_time
=
1271 let elapsed_ms = Float.(Unix.gettimeofday
() - start_time
) *. 1000. in
1273 "%s: %s - %0.2fms\n"
1274 (Relative_path.to_absolute fn
|> Filename.basename
)
1278 let check_file ctx errors files_info ~
profile_type_check_multi ~
memtrace =
1279 let profiling = Option.is_some
profile_type_check_multi in
1281 Relative_path.Map.iter files_info ~f
:(fun fn fileinfo
->
1282 let start_time = Unix.gettimeofday
() in
1283 let _ = Typing_check_utils.type_file ctx fn fileinfo
in
1284 print_elapsed fn
"first typecheck+decl" ~
start_time);
1286 Option.map
memtrace ~f
:(fun filename
->
1287 Memtrace.start_tracing
1289 ~sampling_rate
:Memtrace.default_sampling_rate
1292 let add_timing fn timings closure
=
1293 let start_cpu = Sys.time
() in
1294 let result = Lazy.force closure
in
1295 let elapsed_cpu_time = Sys.time
() -. start_cpu in
1296 let add_sample = function
1297 | Some samples
-> Some
(elapsed_cpu_time :: samples
)
1298 | None
-> Some
[elapsed_cpu_time]
1300 let timings = Relative_path.Map.update fn
add_sample timings in
1303 let rec go n
timings =
1304 let (errors
, timings) =
1305 Relative_path.Map.fold
1307 ~f
:(fun fn fileinfo
(errors
, timings) ->
1308 let ((_, new_errors
), timings) =
1309 add_timing fn
timings
1310 @@ lazy (Typing_check_utils.type_file ctx fn fileinfo
)
1312 (errors
@ Errors.get_sorted_error_list new_errors
, timings))
1313 ~init
:(errors
, timings)
1320 let n_of_times_to_typecheck =
1321 max
1 (Option.value ~default
:1 profile_type_check_multi)
1323 let timings = Relative_path.Map.empty
in
1324 let (errors
, timings) = go n_of_times_to_typecheck timings in
1325 let print_elapsed_cpu_time fn samples
=
1326 let mean = mean samples
in
1328 "%s: %d typechecks - %f ± %f (s)\n"
1329 (Relative_path.to_absolute fn
|> Filename.basename
)
1330 n_of_times_to_typecheck
1332 (standard_deviation mean samples
)
1334 if profiling then Relative_path.Map.iter
timings ~f
:print_elapsed_cpu_time;
1335 Option.iter
tracer ~f
:Memtrace.stop_tracing
;
1338 let create_nasts ctx files_info
=
1339 let build_nast fn
_ =
1340 let (syntax_errors
, ast
) =
1341 Ast_provider.get_ast_with_error ~full
:true ctx fn
1343 let error_list = Errors.get_sorted_error_list syntax_errors
in
1344 List.iter
error_list ~f
:Errors.add_error
;
1345 Naming.program ctx ast
1347 Relative_path.Map.mapi ~f
:build_nast files_info
1349 (** This is an almost-pure function which returns what we get out of parsing.
1350 The only side-effect it has is on the global errors list. *)
1351 let parse_and_name ctx files_contents
=
1352 Relative_path.Map.mapi files_contents ~f
:(fun fn contents
->
1353 (* Get parse errors. *)
1355 Errors.run_in_context fn
Errors.Parsing
(fun () ->
1356 let popt = Provider_context.get_tcopt ctx
in
1358 Full_fidelity_ast.defensive_program
popt fn contents
1361 let { Parser_return.ast; _ } = parsed_file in
1362 if ParserOptions.deregister_php_stdlib
popt then
1363 Nast.deregister_ignored_attributes
ast
1367 Ast_provider.provide_ast_hint fn
ast Ast_provider.Full
;
1370 match Direct_decl_utils.direct_decl_parse ctx fn
with
1371 | None
-> failwith
"no file contents"
1372 | Some decls
-> Direct_decl_utils.decls_to_fileinfo fn decls
)
1374 (** This function is used for gathering naming and parsing errors,
1375 and the side-effect of updating the global reverse naming table (and
1376 picking up duplicate-name errors along the way), and for the side effect
1377 of updating the decl heap (and picking up decling errors along the way). *)
1378 let parse_name_and_decl ctx files_contents
=
1379 Errors.do_
(fun () ->
1380 let files_info = parse_and_name ctx files_contents
in
1381 Relative_path.Map.iter
files_info ~f
:(fun fn fileinfo
->
1382 let (errors
, _failed_naming_fns
) =
1383 Naming_global.ndecl_file_error_if_already_bound ctx fn fileinfo
1385 Errors.merge_into_current errors
);
1386 Relative_path.Map.iter
files_info ~f
:(fun fn
_ ->
1387 Errors.run_in_context fn
Errors.Decl
(fun () ->
1388 Decl.make_env ~sh
:SharedMem.Uses ctx fn
));
1392 (** This function is used solely for its side-effect of putting decls into shared-mem *)
1393 let add_decls_to_heap ctx files_contents
=
1394 Errors.ignore_
(fun () ->
1395 let files_info = parse_and_name ctx files_contents
in
1396 Relative_path.Map.iter
files_info ~f
:(fun fn
_ ->
1397 Errors.run_in_context fn
Errors.Decl
(fun () ->
1398 Decl.make_env ~sh
:SharedMem.Uses ctx fn
)));
1401 (** This function doesn't have side-effects. Its sole job is to return shallow decls. *)
1402 let get_shallow_decls ctx filename file_contents
:
1403 Shallow_decl_defs.shallow_class
SMap.t
=
1404 let popt = Provider_context.get_popt ctx
in
1405 let opts = DeclParserOptions.from_parser_options
popt in
1406 (Direct_decl_parser.parse_decls
opts filename file_contents
)
1407 .Direct_decl_parser.pf_decls
1408 |> List.fold ~init
:SMap.empty ~f
:(fun acc
(name
, decl
) ->
1410 | Shallow_decl_defs.Class c
-> SMap.add name c acc
1413 let test_shallow_class_diff popt filename
=
1414 let filename_after = Relative_path.to_absolute filename ^
".after" in
1415 let contents1 = Sys_utils.cat
(Relative_path.to_absolute filename
) in
1416 let contents2 = Sys_utils.cat
filename_after in
1417 let decls1 = get_shallow_decls popt filename
contents1 in
1418 let decls2 = get_shallow_decls popt filename
contents2 in
1420 SMap.merge
(fun _ a b
-> Some
(a
, b
)) decls1 decls2 |> SMap.bindings
1423 List.map
decls ~f
:(fun (cid
, old_and_new
) ->
1424 ( Utils.strip_ns cid
,
1425 match old_and_new
with
1426 | (Some c1
, Some c2
) -> Shallow_class_diff.diff_class c1 c2
1427 | (None
, None
) -> ClassDiff.(Major_change
MajorChange.Unknown
)
1428 | (None
, Some
_) -> ClassDiff.(Major_change
MajorChange.Added
)
1429 | (Some
_, None
) -> ClassDiff.(Major_change
MajorChange.Removed
) ))
1431 List.iter
diffs ~f
:(fun (cid
, diff) ->
1432 Format.printf
"%s: %a@." cid
ClassDiff.pp
diff)
1434 let add_newline contents
=
1435 (* this is used for incremental mode to change all the positions, so we
1436 basically want a prepend; there's a few cases we need to handle:
1438 - header line: apppend after header
1439 - shebang and header: append after header
1440 - shebang only, no header (e.g. .hack file): append after shebang
1441 - no header or shebang (e.g. .hack file): prepend
1444 if string_starts_with contents
"#!" then
1445 String.index_exn contents '
\n'
+ 1
1451 String.length contents
> after_shebang + 2
1452 && String.equal
(String.sub contents ~pos
:after_shebang ~len
:2) "<?"
1454 String.index_from_exn contents
after_shebang '
\n'
+ 1
1458 String.sub contents ~pos
:0 ~len
:after_header
1463 ~len
:(String.length contents
- after_header)
1465 (* Might raise because of Option.value_exn *)
1466 let get_decls defs
=
1469 Option.value_exn ~message
:"Decl not found" (Decl_heap.Typedefs.get x
)
1471 defs
.FileInfo.n_types
1475 Option.value_exn ~message
:"Decl not found" (Decl_heap.Funs.get x
) :: acc
)
1476 defs
.FileInfo.n_funs
1480 Option.value_exn ~message
:"Decl not found" (Decl_heap.Classes.get x
)
1482 defs
.FileInfo.n_classes
1485 let fail_comparison s
=
1488 (Printf.sprintf
"Comparing %s failed!\n" s
1489 ^
"It's likely that you added new positions to decl types "
1490 ^
"without updating Decl_pos_utils.NormalizeSig\n"))
1492 let compare_typedefs t1 t2
=
1493 let t1 = Decl_pos_utils.NormalizeSig.typedef
t1 in
1494 let t2 = Decl_pos_utils.NormalizeSig.typedef
t2 in
1495 if Poly.(t1 <> t2) then fail_comparison "typedefs"
1497 let compare_funs f1 f2
=
1498 let f1 = Decl_pos_utils.NormalizeSig.fun_elt
f1 in
1499 let f2 = Decl_pos_utils.NormalizeSig.fun_elt
f2 in
1500 if Poly.(f1 <> f2) then fail_comparison "funs"
1502 let compare_classes mode c1 c2
=
1503 if Decl_compare.class_big_diff c1 c2
then fail_comparison "class_big_diff";
1505 let c1 = Decl_pos_utils.NormalizeSig.class_type
c1 in
1506 let c2 = Decl_pos_utils.NormalizeSig.class_type
c2 in
1507 let (_, is_unchanged
) =
1508 Decl_compare.ClassDiff.compare
mode c1.Decl_defs.dc_name
c1 c2
1510 if not is_unchanged
then fail_comparison "ClassDiff";
1512 let (_, is_unchanged
) = Decl_compare.ClassEltDiff.compare
mode c1 c2 in
1513 match is_unchanged
with
1514 | `Changed
-> fail_comparison "ClassEltDiff"
1517 let test_decl_compare ctx filenames builtins files_contents
files_info =
1518 (* skip some edge cases that we don't handle now... ugly! *)
1519 if String.equal
(Relative_path.suffix filenames
) "capitalization3.php" then
1521 else if String.equal
(Relative_path.suffix filenames
) "capitalization4.php"
1525 (* do not analyze builtins over and over *)
1527 Relative_path.Map.fold
1531 (fun k
_ acc
-> Relative_path.Map.remove acc k
)
1536 Relative_path.Map.fold
1538 ~f
:(fun k
_ acc
-> Relative_path.Set.add acc k
)
1539 ~init
:Relative_path.Set.empty
1542 Relative_path.Map.fold
1546 fun _ names1 names2
->
1547 FileInfo.(merge_names
(simplify names1
) names2
)
1549 ~init
:FileInfo.empty_names
1551 let (typedefs1
, funs1
, classes1
) = get_decls defs in
1552 (* For the purpose of this test, we can ignore other heaps *)
1553 Ast_provider.remove_batch
files;
1555 let get_classes path
=
1556 match Relative_path.Map.find_opt
files_info path
with
1557 | None
-> SSet.empty
1559 SSet.of_list
@@ List.map info
.FileInfo.classes ~f
:(fun (_, x
, _) -> x
)
1561 (* We need to oldify, not remove, for ClassEltDiff to work *)
1562 Decl_redecl_service.oldify_type_decl
1568 ~collect_garbage
:false;
1570 let files_contents = Relative_path.Map.map
files_contents ~f
:add_newline in
1571 add_decls_to_heap ctx
files_contents;
1572 let (typedefs2
, funs2
, classes2
) = get_decls defs in
1573 let deps_mode = Provider_context.get_deps_mode ctx
in
1574 List.iter2_exn typedefs1 typedefs2 ~f
:compare_typedefs;
1575 List.iter2_exn funs1 funs2 ~f
:compare_funs;
1576 List.iter2_exn classes1 classes2 ~f
:(compare_classes deps_mode);
1579 (* Returns a list of Tast defs, along with associated type environments. *)
1580 let compute_tasts ?
(drop_fixmed
= true) ctx
files_info interesting_files
:
1582 * (Tast.program
Relative_path.Map.t
1583 * Typing_inference_env.t_global_with_pos list
) =
1585 match (nast
, x
) with
1586 | (Some nast
, Some
_) -> Some nast
1589 Errors.do_ ~drop_fixmed
(fun () ->
1590 let nasts = create_nasts ctx
files_info in
1591 (* Interesting files are usually the non hhi ones. *)
1592 let filter_non_interesting nasts =
1593 Relative_path.Map.merge
nasts interesting_files ~f
:(fun _k nast x
->
1594 match (nast
, x
) with
1595 | (Some nast
, Some
_) -> Some nast
1598 let nasts = filter_non_interesting nasts in
1600 Relative_path.Map.map
1602 ~f
:(Typing_toplevel.nast_to_tast_gienv ~do_tast_checks
:true ctx
)
1604 let tasts = Relative_path.Map.map
tasts_envs ~f
:fst
in
1607 @@ Relative_path.Map.values
1608 @@ Relative_path.Map.map
tasts_envs ~f
:snd
1612 let merge_global_inference_env_in_tast gienv tast
=
1615 inherit Tast_visitor.endo
1617 method! on_'en
_ env =
1620 Tast.inference_env
=
1621 Typing_inference_env.simple_merge
1622 env.Tast.inference_env
1623 gienv.inference_env
;
1629 (* Given source code containing the string "^ hover-at-caret", return
1630 the line and column of the position indicated. *)
1631 let hover_at_caret_pos (src
: string) : int * int =
1632 let lines = String.split_lines src
in
1634 List.findi
lines ~f
:(fun _ line ->
1635 String.is_substring
line ~substring
:"^ hover-at-caret")
1637 | Some
(line_num
, line_src
) ->
1639 String.lfindi line_src ~f
:(fun _ c
->
1644 (line_num
, Option.value_exn
col_num + 1)
1646 failwith
"Could not find any occurrence of ^ hover-at-caret in source code"
1649 * Compute TASTs for some files, then expand all type variables.
1651 let compute_tasts_expand_types ctx ~
verbosity files_info interesting_files
=
1652 let (errors
, (tasts, gienvs)) =
1653 compute_tasts ctx
files_info interesting_files
1655 let subconstraints =
1656 Typing_global_inference.StateSubConstraintGraphs.build
1658 (List.concat
(Relative_path.Map.values
tasts))
1661 let (tasts, gi_solved
) =
1662 match global_inference_merge_and_solve ctx ~
verbosity subconstraints with
1663 | None
-> (tasts, None
)
1664 | Some
((gienv, _, _) as gi_solved
) ->
1666 Relative_path.Map.map
1668 ~f
:(merge_global_inference_env_in_tast gienv ctx
)
1670 (tasts, Some gi_solved
)
1672 let tasts = Relative_path.Map.map
tasts ~f
:(Tast_expand.expand_program ctx
) in
1673 (errors
, tasts, gi_solved
)
1675 let decl_parse_typecheck_and_then ~
verbosity ctx
files_contents f
=
1676 let (parse_errors
, files_info) = parse_name_and_decl ctx
files_contents in
1677 let parse_errors = Errors.get_sorted_error_list
parse_errors in
1678 let (errors
, _tasts
, _gi_solved
) =
1679 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
1681 let errors = parse_errors @ Errors.get_sorted_error_list
errors in
1682 if List.is_empty
errors then
1685 print_errors_if_present errors
1687 let print_nasts ~should_print_position
nasts filenames
=
1688 List.iter filenames ~f
:(fun filename
->
1689 match Relative_path.Map.find_opt
nasts filename
with
1692 "Could not find nast for file %s\n"
1693 (Relative_path.show filename
);
1694 Printf.eprintf
"Available nasts:\n";
1695 Relative_path.Map.iter
nasts ~f
:(fun path
_ ->
1696 Printf.eprintf
" %s\n" (Relative_path.show path
))
1698 if should_print_position
then
1699 Naming_ast_print.print_nast nast
1701 Naming_ast_print.print_nast_without_position nast
)
1703 let print_tasts ~should_print_position
tasts ctx
=
1704 Relative_path.Map.iter
tasts ~f
:(fun _k
(tast
: Tast.program
) ->
1705 if should_print_position
then
1706 Typing_ast_print.print_tast ctx tast
1708 Typing_ast_print.print_tast_without_position ctx tast
)
1710 let typecheck_tasts tasts tcopt (filename
: Relative_path.t
) =
1711 let env = Typing_env_types.empty
tcopt filename ~droot
:None
in
1712 let tasts = Relative_path.Map.values
tasts in
1713 let typecheck_tast tast
=
1714 Errors.get_sorted_error_list
(Tast_typecheck.check
env tast
)
1716 List.concat_map
tasts ~f
:typecheck_tast
1718 let pp_debug_deps fmt entries
=
1719 Format.fprintf fmt
"@[<v>";
1721 @@ List.fold_left entries ~init
:false ~f
:(fun sep
(obj
, roots
) ->
1722 if sep
then Format.fprintf fmt
"@;";
1723 Format.fprintf fmt
"%s -> " obj
;
1724 Format.fprintf fmt
"@[<hv>";
1726 @@ List.fold_left roots ~init
:false ~f
:(fun sep
root ->
1727 if sep
then Format.fprintf fmt
",@ ";
1728 Format.pp_print_string fmt
root;
1730 Format.fprintf fmt
"@]";
1732 Format.fprintf fmt
"@]"
1734 let show_debug_deps = Format.asprintf
"%a" pp_debug_deps
1736 let sort_debug_deps deps
=
1737 Hashtbl.fold deps ~init
:[] ~f
:(fun ~key
:obj ~data
:set acc
->
1739 |> List.sort ~compare
:(fun (a
, _) (b
, _) -> String.compare a b
)
1740 |> List.map ~f
:(fun (obj
, roots
) ->
1742 HashSet.fold
roots ~init
:[] ~f
:List.cons
1743 |> List.sort ~compare
:String.compare
1747 (* Note: this prints dependency graph edges in the same direction as the mapping
1748 which is actually stored in the shared memory table. The line "X -> Y" can be
1749 read, "X is used by Y", or "X is a dependency of Y", or "when X changes, Y
1750 must be rechecked". *)
1751 let dump_debug_deps dbg_deps
=
1752 dbg_deps
|> sort_debug_deps |> show_debug_deps |> Printf.printf
"%s\n"
1754 let dump_debug_glean_deps
1756 (Typing_deps.Dep.dependency
Typing_deps.Dep.variant
1757 * Typing_deps.Dep.dependent
Typing_deps.Dep.variant
)
1759 let json_opt = Glean_dependency_graph_convert.convert_deps_to_json ~deps
in
1762 Printf.printf
"%s\n" (Hh_json.json_to_string ~pretty
:true json_obj)
1763 | None
-> Printf.printf
"No dependencies\n"
1765 let handle_constraint_mode
1772 ~
profile_type_check_multi
1774 (* Process a single typechecked file *)
1775 let process_file path info
=
1776 match info
.FileInfo.file_mode
with
1777 | Some
FileInfo.Mstrict
->
1778 let (ctx
, entry
) = Provider_context.add_entry_if_missing ~ctx ~path
in
1779 let { Tast_provider.Compute_tast.tast
; _ } =
1780 Tast_provider.compute_tast_unquarantined ~ctx ~entry
1784 (* We are not interested in partial files and there is nothing in HHI
1788 let print_errors = List.iter ~f
:(print_error ~
oc:stdout
error_format) in
1789 (* Process a multifile that is not typechecked *)
1790 let process_multifile filename
=
1792 "=== %s analysis results for %s\n%!"
1794 (Relative_path.to_absolute filename
);
1795 let files_contents = Multifile.file_to_files filename
in
1796 let (parse_errors, file_info
) = parse_name_and_decl ctx
files_contents in
1797 let error_list = Errors.get_sorted_error_list
parse_errors in
1799 check_file ctx
error_list file_info ~
profile_type_check_multi ~
memtrace
1801 if not
(List.is_empty
check_errors) then
1802 print_errors check_errors
1804 Relative_path.Map.iter file_info ~f
:process_file
1806 let process_multifile filename
=
1807 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f
:(fun () ->
1808 process_multifile filename
)
1810 iter_over_files
process_multifile
1812 let scrape_class_names (ast : Nast.program
) : SSet.t
=
1813 let names = ref SSet.empty
in
1816 (* It would look less clumsy to use Aast.reduce, but would use set union which has higher complexity. *)
1817 inherit [_] Aast.iter
1819 method! on_class_name
_ (_p
, id
) = names := SSet.add id
!names
1822 visitor#on_program
() ast;
1825 (** Scrape names in file and return the files where those names are defined. *)
1826 let get_some_file_dependencies ctx
(file
: Relative_path.t
) :
1827 Relative_path.Set.t
=
1828 let open Hh_prelude
in
1829 let nast = Ast_provider.get_ast ctx ~full
:true file
in
1831 Errors.ignore_
(fun () -> Naming.program ctx
nast) |> scrape_class_names
1832 (* TODO: scape other defs too *)
1835 (fun class_name
files ->
1836 match Naming_provider.get_class_path ctx class_name
with
1838 | Some file
-> Relative_path.Set.add
files file
)
1840 Relative_path.Set.empty
1842 (** Recursively scrape names in files and return the
1843 files where those names are defined. *)
1844 let traverse_file_dependencies ctx
(files : Relative_path.t list
) ~
(depth
: int)
1845 : Relative_path.Set.t
=
1847 (files : Relative_path.Set.t
)
1849 (visited
: Relative_path.Set.t
)
1850 (results : Relative_path.Set.t
) =
1851 if Int.( <= ) depth
0 then
1852 Relative_path.Set.union
files results
1854 let (next_files
, visited
, results) =
1855 Relative_path.Set.fold
1857 ~init
:(Relative_path.Set.empty
, visited
, results)
1858 ~f
:(fun file
(next_files
, visited
, results) ->
1859 if Relative_path.Set.mem visited file
then
1860 (next_files
, visited
, results)
1862 let visited = Relative_path.Set.add
visited file
in
1863 let dependencies = get_some_file_dependencies ctx file
in
1865 Relative_path.Set.union
dependencies next_files
1867 let results = Relative_path.Set.add
results file
in
1868 (next_files, visited, results))
1870 traverse next_files (depth
- 1) visited results
1873 (Relative_path.Set.of_list
files)
1875 Relative_path.Set.empty
1876 Relative_path.Set.empty
1878 let apply_patches files_contents patches
=
1879 if List.length patches
<= 0 then
1880 print_endline
"No patches"
1882 ServerRefactorTypes.apply_patches_to_file_contents
files_contents patches
1883 |> Multifile.print_files_as_multifile
1899 ~should_print_position
1900 ~
profile_type_check_multi
1903 let expect_single_file () : Relative_path.t
=
1904 match filenames
with
1906 | _ -> die "Only single file expected"
1908 let iter_over_files f
: unit = List.iter filenames ~f
in
1910 | Refactor_sound_dynamic
(analysis_mode
, refactor_mode, element_name
) ->
1913 ( Refactor_sd_options.parse_analysis_mode analysis_mode
,
1914 Refactor_sd_options.parse_refactor_mode
refactor_mode )
1916 | (Some analysis_mode
, Some
refactor_mode) ->
1917 Refactor_sd_options.mk ~analysis_mode ~
refactor_mode
1918 | (None
, _) -> die "invalid refactor_sd analysis mode"
1919 | (_, None
) -> die "invalid refactor_sd refactor mode"
1921 handle_constraint_mode
1922 ~do_
:(Refactor_sd.do_ element_name
)
1928 ~
profile_type_check_multi
1930 | SDT_analysis command
->
1933 match Sdt_analysis_options.parse_command
command with
1934 | Some
command -> command
1935 | None
-> die "invalid SDT analysis mode"
1937 Sdt_analysis_options.mk ~
verbosity ~
command
1939 handle_constraint_mode
1940 ~do_
:Sdt_analysis.do_
1946 ~
profile_type_check_multi
1948 | Shape_analysis
mode ->
1950 match Shape_analysis_options.parse_mode
mode with
1951 | Some
(command, mode) ->
1952 Shape_analysis_options.mk ~
command ~
mode ~
verbosity
1953 | None
-> die "invalid shape analysis mode"
1955 handle_constraint_mode
1956 ~do_
:Shape_analysis.do_
1962 ~
profile_type_check_multi
1964 | Ifc
(mode, lattice
) ->
1965 (* Timing mode is same as check except we print out the time it takes to
1966 analyse the file. *)
1967 let (mode, should_time
) =
1968 if String.equal
mode "time" then
1974 match Ifc_options.parse ~
mode ~lattice
with
1976 | Error e
-> die ("could not parse IFC options: " ^ e
)
1979 let start_time = Unix.gettimeofday
() in
1980 let result = Lazy.force f
in
1981 let elapsed_time = Unix.gettimeofday
() -. start_time in
1982 if should_time
then Printf.printf
"Duration: %f\n" elapsed_time;
1985 let print_errors = List.iter ~f
:(print_error ~
oc:stdout
error_format) in
1986 let process_file filename
=
1988 "=== IFC analysis results for %s\n%!"
1989 (Relative_path.to_absolute filename
);
1990 let files_contents = Multifile.file_to_files filename
in
1991 let (parse_errors, file_info
) = parse_name_and_decl ctx
files_contents in
1993 let error_list = Errors.get_sorted_error_list
parse_errors in
1994 check_file ctx
error_list file_info ~
profile_type_check_multi ~
memtrace
1996 if not
(List.is_empty
check_errors) then
1997 print_errors check_errors
2000 let ifc_errors = time @@ lazy (Ifc_main.do_
ifc_opts file_info ctx
) in
2001 if not
(List.is_empty
ifc_errors) then print_errors ifc_errors
2004 let e = Exception.wrap exn
in
2005 Stdlib.Printexc.register_printer
(function
2006 | Ifc_types.IFCError err
->
2008 (Printf.sprintf
"IFCError(%s)"
2009 @@ Ifc_types.show_ifc_error_ty err
)
2011 Printf.printf
"Uncaught exception: %s" (Exception.to_string
e)
2013 iter_over_files (fun filename
->
2014 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f
:(fun () ->
2015 process_file filename
))
2017 Relative_path.Map.iter
files_info ~f
:(fun fn fileinfo
->
2018 if Relative_path.Map.mem builtins fn
then
2021 let (tast
, _) = Typing_check_utils.type_file ctx fn fileinfo
in
2022 let result = Coverage_level.get_levels ctx tast fn
in
2024 | Ok
result -> print_colored fn
result
2027 ("HH_FIXMEs not found for path " ^
Relative_path.to_absolute fn
))
2029 Relative_path.Map.iter
files_info ~f
:(fun fn fileinfo
->
2030 if Relative_path.Map.mem builtins fn
then
2033 let (tast
, _) = Typing_check_utils.type_file ctx fn fileinfo
in
2035 ServerCoverageMetricUtils.accumulate_types ctx tast fn
2037 print_coverage type_acc)
2039 let path = expect_single_file () in
2040 let (ctx
, entry
) = Provider_context.add_entry_if_missing ~ctx ~
path in
2042 let open Result.Monad_infix
in
2043 Sys_utils.read_stdin_to_string
()
2044 |> Hh_json.json_of_string
2045 |> CstSearchService.compile_pattern ctx
2046 >>| CstSearchService.search ctx entry
2047 >>| CstSearchService.result_to_json ~sort_results
:true
2048 >>| Hh_json.json_to_string ~pretty
:true
2052 | Ok
result -> Printf.printf
"%s\n" result
2054 Printf.printf
"%s\n" message
;
2057 | Dump_symbol_info
->
2058 iter_over_files (fun filename
->
2059 match Relative_path.Map.find_opt
files_info filename
with
2061 let raw_result = SymbolInfoServiceUtils.helper ctx
[] [filename
] in
2062 let result = SymbolInfoServiceUtils.format_result
raw_result in
2064 ServerCommandTypes.Symbol_info_service.to_json
result
2066 print_endline
(Hh_json.json_to_multiline
result_json)
2068 | Glean_index out_dir
->
2070 (not
(Disk.is_directory out_dir
))
2071 || Array.length
(Sys.readdir out_dir
) > 0
2073 Printf.printf
"%s should be an empty dir\n" out_dir
;
2076 Symbol_entrypoint.index_files ctx ~out_dir ~
files:filenames
2079 Relative_path.Map.fold
2082 ~f
:(fun fn
content lint_errors ->
2084 @ fst
(Lints_core.do_
(fun () -> Linting_main.lint ctx fn
content)))
2086 if not
(List.is_empty
lint_errors) then (
2092 Pos.compare
(Lints_core.get_pos x
) (Lints_core.get_pos y
)
2096 let lint_errors = List.map ~f
:Lints_core.to_absolute
lint_errors in
2097 ServerLintTypes.output_text stdout
lint_errors error_format;
2100 Printf.printf
"No lint errors\n"
2103 Relative_path.Map.fold
2106 ~f
:(fun fn
content json_errors ->
2108 @ fst
(Lints_core.do_
(fun () -> Linting_main.lint ctx fn
content)))
2115 Pos.compare
(Lints_core.get_pos x
) (Lints_core.get_pos y
)
2119 let json_errors = List.map ~f
:Lints_core.to_absolute
json_errors in
2120 ServerLintTypes.output_json ~pretty
:true stdout
json_errors;
2123 Relative_path.Map.iter
files_info ~f
:(fun fn fileinfo
->
2124 ignore
@@ Typing_check_utils.check_defs ctx fn fileinfo
);
2125 if Hashtbl.length dbg_deps
> 0 then dump_debug_deps dbg_deps
2126 | Dump_dep_hashes
->
2127 iter_over_files (fun _ ->
2128 let nasts = create_nasts ctx
files_info in
2129 Relative_path.Map.iter
nasts ~f
:(fun _ nast ->
2130 Dep_hash_to_symbol.dump
nast))
2131 | Dump_glean_deps
->
2132 Relative_path.Map.iter
files_info ~f
:(fun fn fileinfo
->
2133 ignore
@@ Typing_check_utils.check_defs ctx fn fileinfo
);
2134 dump_debug_glean_deps dbg_glean_deps
2135 | Get_some_file_deps depth
->
2136 let file_deps = traverse_file_dependencies ctx filenames ~depth
in
2137 Relative_path.Set.iter
file_deps ~f
:(fun file
->
2138 Printf.printf
"%s\n" (Relative_path.to_absolute file
))
2139 | Dump_inheritance
->
2140 let open ServerCommandTypes.Method_jumps
in
2141 let naming_table = Naming_table.create
files_info in
2142 Naming_table.iter
naming_table ~f
:(fun fn fileinfo
->
2143 if Relative_path.Map.mem builtins fn
then
2146 List.iter fileinfo
.FileInfo.classes ~f
:(fun (_p
, class_
, _) ->
2148 "Ancestors of %s and their overridden methods:\n"
2151 (* Might raise {!Naming_table.File_info_not_found} *)
2152 MethodJumps.get_inheritance
2156 ~find_children
:false
2160 ServerCommandTypes.Method_jumps.print_readable
2162 ~find_children
:false;
2163 Printf.printf
"\n");
2165 List.iter fileinfo
.FileInfo.classes ~f
:(fun (_p
, class_
, _) ->
2167 "Children of %s and the methods they override:\n"
2170 (* Might raise {!Naming_table.File_info_not_found} *)
2171 MethodJumps.get_inheritance
2179 ServerCommandTypes.Method_jumps.print_readable
2181 ~find_children
:true;
2184 | Identify_symbol
(line, column
) ->
2185 let path = expect_single_file () in
2186 let (ctx
, entry
) = Provider_context.add_entry_if_missing ~ctx ~
path in
2187 (* TODO(ljw): surely this doesn't need quarantine? *)
2189 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f
:(fun () ->
2190 ServerIdentifyFunction.go_quarantined_absolute
2198 | [] -> print_endline
"None"
2199 | result -> ClientGetDefinition.print_readable ~short_pos
:true result
2201 | Find_local
(line, char
) ->
2202 let filename = expect_single_file () in
2204 Provider_context.add_entry_if_missing ~ctx ~
path:filename
2206 let result = ServerFindLocals.go ~ctx ~entry ~
line ~char
in
2207 let print pos
= Printf.printf
"%s\n" (Pos.string_no_file pos
) in
2208 List.iter
result ~f
:print
2210 iter_over_files (fun filename ->
2211 let file = cat
(Relative_path.to_absolute
filename) in
2213 FileOutline.outline
(Provider_context.get_popt ctx
) file
2215 FileOutline.print ~short_pos
:true results)
2217 let (errors, nasts) = Errors.do_
(fun () -> create_nasts ctx
files_info) in
2218 print_errors_if_present (Errors.get_sorted_error_list
errors);
2221 ~should_print_position
2223 (Relative_path.Map.keys
files_contents)
2225 let (errors, tasts, _gi_solved
) =
2226 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
2228 print_errors_if_present (parse_errors @ Errors.get_sorted_error_list
errors);
2229 print_tasts ~should_print_position
tasts ctx
2231 iter_over_files (fun filename ->
2232 let files_contents =
2233 Relative_path.Map.filter
files_contents ~f
:(fun k _v
->
2234 Relative_path.equal k
filename)
2236 let (errors, tasts, _gi_solved
) =
2237 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
2239 print_tasts ~should_print_position
tasts ctx
;
2240 if not
@@ Errors.is_empty
errors then (
2241 print_errors error_format errors max_errors;
2242 Printf.printf
"Did not typecheck the TAST as there are typing errors.";
2245 let tast_check_errors = typecheck_tasts tasts ctx
filename in
2246 print_error_list error_format tast_check_errors max_errors;
2247 if not
(List.is_empty
tast_check_errors) then exit
2)
2248 | Dump_stripped_tast
->
2249 iter_over_files (fun filename ->
2250 let files_contents =
2251 Relative_path.Map.filter
files_contents ~f
:(fun k _v
->
2252 Relative_path.equal k
filename)
2254 let (_, (tasts, _gienvs
)) =
2255 compute_tasts ctx
files_info files_contents
2257 let tast = Relative_path.Map.find
tasts filename in
2258 let nast = Tast.to_nast
tast in
2259 Printf.printf
"%s\n" (Nast.show_program
nast))
2260 | RewriteGlobalInference
->
2261 let (errors, _tasts
, gi_solved
) =
2262 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
2264 print_errors_if_present (parse_errors @ Errors.get_sorted_error_list
errors);
2265 (match gi_solved
with
2268 ("error: no patches generated as global"
2269 ^
" inference is turend off (use --global-inference)");
2272 ServerGlobalInference.Mode_rewrite.get_patches ~
files_contents gi_solved
2273 |> apply_patches files_contents)
2274 | RemoveDeadUnsafeCasts
->
2276 Provider_context.map_tcopt
ctx ~f
:(fun tcopt ->
2277 GlobalOptions.{ tcopt with tco_populate_dead_unsafe_cast_heap
= true })
2279 let decl_parse_typecheck_and_then =
2280 decl_parse_typecheck_and_then ~
verbosity ctx
2282 let backend = Provider_context.get_backend
ctx in
2283 (* Because we repeatedly apply the codemod, positions change. So we need to
2284 re-parse, decl, and typecheck the file to generated accurate patches as
2285 well as amend the patched test files in memory. This involves
2286 invalidating a number of shared heaps and providing in memory
2287 replacements after patching files. *)
2288 let invalidate_heaps_and_update_files files_info files_contents =
2289 let paths_to_purge =
2290 Relative_path.Map.keys
files_contents |> Relative_path.Set.of_list
2292 (* Purge the file, then provide its replacement, otherwise the
2293 replacement is dropped on the floor. *)
2294 File_provider.remove_batch
paths_to_purge;
2295 Relative_path.Map.iter
2296 ~f
:File_provider.provide_file_for_tests
2298 Ast_provider.remove_batch
paths_to_purge;
2299 Relative_path.Map.iter
2300 ~f
:(fun path file_info
->
2301 (* Don't invalidate builtins, otherwise, we can't find them. *)
2302 if not
(Relative_path.prefix
path |> Relative_path.is_hhi
) then
2303 Naming_global.remove_decls_using_file_info
backend file_info
)
2306 (* Repeatedly apply dead unsafe cast removal. We can't do this at once as
2307 removing one unsafe cast might have a bearing on another. *)
2308 let rec go files_info files_contents =
2309 invalidate_heaps_and_update_files files_info files_contents;
2310 decl_parse_typecheck_and_then files_contents @@ fun files_info ->
2312 Remove_dead_unsafe_casts.get_patches
2315 ~fold
:Relative_path.Map.fold
2317 let files_contents =
2318 ServerRefactorTypes.apply_patches_to_file_contents
2322 if List.is_empty
patches then
2323 Multifile.print_files_as_multifile
files_contents
2325 go files_info files_contents
2327 go files_info files_contents;
2329 (* Typecheck after the codemod is fully applied to confirm that what we
2330 produce is not garbage. *)
2332 "\nTypechecking after the codemod... (no output after this is good news)\n";
2333 invalidate_heaps_and_update_files files_info files_contents;
2334 decl_parse_typecheck_and_then files_contents @@ fun _ -> ()
2335 | Find_refs
(line, column
) ->
2336 let path = expect_single_file () in
2337 let naming_table = Naming_table.create
files_info in
2338 let genv = ServerEnvBuild.default_genv
in
2339 let init_id = Random_id.short_string
() in
2342 (ServerEnvBuild.make_env
2344 ~
deps_mode:(Typing_deps_mode.InMemoryMode None
)
2345 genv.ServerEnv.config
)
2347 ServerEnv.naming_table;
2348 ServerEnv.tcopt = Provider_context.get_tcopt
ctx;
2351 let include_defs = true in
2353 Provider_context.add_entry_if_missing
2354 ~
ctx:(Provider_utils.ctx_from_server_env
env)
2357 let open Option.Monad_infix
in
2358 let open ServerCommandTypes.Done_or_retry
in
2360 Provider_utils.respect_but_quarantine_unsaved_changes ~
ctx ~f
:(fun () ->
2362 go_from_file_ctx ~
ctx ~entry ~
line ~column
>>= fun (name
, action
) ->
2363 go ctx action
include_defs genv env
2364 |> map_env ~f
:(to_ide name
)
2370 @@ "should only happen with prechecked files "
2371 ^
"which are not a thing in hh_single_type_check"))
2373 ClientFindRefsPrint.print_ide_readable
results
2374 | Go_to_impl
(line, column
) ->
2375 let filename = expect_single_file () in
2376 let naming_table = Naming_table.create
files_info in
2377 let genv = ServerEnvBuild.default_genv
in
2378 let init_id = Random_id.short_string
() in
2381 (ServerEnvBuild.make_env
2383 ~
deps_mode:(Typing_deps_mode.InMemoryMode None
)
2384 genv.ServerEnv.config
)
2386 ServerEnv.naming_table;
2387 ServerEnv.tcopt = Provider_context.get_tcopt
ctx;
2390 let filename = Relative_path.to_absolute
filename in
2391 let contents = cat
filename in
2393 Provider_context.add_or_overwrite_entry_contents
2394 ~
ctx:(Provider_utils.ctx_from_server_env
env)
2395 ~
path:(Relative_path.create_detect_prefix
filename)
2398 Option.Monad_infix.(
2399 ServerCommandTypes.Done_or_retry.(
2401 ServerFindRefs.go_from_file_ctx ~
ctx ~entry ~
line ~column
2402 >>= fun (name
, action
) ->
2403 ServerGoToImpl.go ~action ~
genv ~
env
2404 |> map_env ~f
:(ServerFindRefs.to_ide name
)
2410 @@ "should only happen with prechecked files "
2411 ^
"which are not a thing in hh_single_type_check"
2413 ClientFindRefsPrint.print_ide_readable
results))
2414 | Highlight_refs
(line, column
) ->
2415 let path = expect_single_file () in
2416 let (ctx, entry
) = Provider_context.add_entry_if_missing ~
ctx ~
path in
2418 ServerHighlightRefs.go_quarantined ~
ctx ~entry ~
line ~column
2420 ClientHighlightRefs.go results ~output_json
:false
2421 | Errors
when batch_mode ->
2422 (* For each file in our batch, run typechecking serially.
2423 Reset the heaps every time in between. *)
2424 iter_over_files (fun filename ->
2426 Out_channel.create
(Relative_path.to_absolute
filename ^
out_extension)
2428 (* This means builtins had errors, so lets just print those if we see them *)
2429 if not
(List.is_empty
parse_errors) then
2430 (* This closes the out channel *)
2431 write_error_list error_format parse_errors oc max_errors
2433 Typing_log.out_channel
:= oc;
2434 Provider_utils.respect_but_quarantine_unsaved_changes
2437 let files_contents = Multifile.file_to_files
filename in
2438 Relative_path.Map.iter
files_contents ~f
:(fun filename contents ->
2439 File_provider.(provide_file_for_tests
filename contents));
2440 let (parse_errors, individual_file_info
) =
2441 parse_name_and_decl ctx files_contents
2446 (Errors.get_sorted_error_list
parse_errors)
2447 individual_file_info
2448 ~
profile_type_check_multi
2451 write_error_list error_format errors oc max_errors)
2453 | Decl_compare
when batch_mode ->
2454 (* For each file in our batch, run typechecking serially.
2455 Reset the heaps every time in between. *)
2456 iter_over_files (fun filename ->
2458 Out_channel.create
(Relative_path.to_absolute
filename ^
".decl_out")
2460 Provider_utils.respect_but_quarantine_unsaved_changes ~
ctx ~f
:(fun () ->
2461 let files_contents =
2462 Relative_path.Map.filter
files_contents ~f
:(fun k _v
->
2463 Relative_path.equal k
filename)
2465 let (_, individual_file_info
) =
2466 parse_name_and_decl ctx files_contents
2474 individual_file_info
;
2475 Out_channel.output_string
oc ""
2478 let msg = Exn.to_string
e in
2479 Out_channel.output_string
oc msg);
2480 Out_channel.close
oc)
2482 (* Don't typecheck builtins *)
2484 check_file ctx parse_errors files_info ~
profile_type_check_multi ~
memtrace
2486 print_error_list error_format errors max_errors;
2487 if not
(List.is_empty
errors) then exit
2
2490 match filenames
with
2492 Stream.from
(fun _ ->
2495 (let path = Caml.input_line
Caml.stdin
|> String.strip
in
2496 Relative_path.(create Dummy
path))
2498 | End_of_file
-> None
)
2499 | filenames
-> Stream.of_list filenames
2502 let (errors, tasts, _gi_solved
) =
2503 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
2505 let errors = Errors.get_error_list
errors in
2506 if (not
(List.is_empty
parse_errors)) || not
(List.is_empty
errors) then begin
2507 List.iter ~f
:(print_error error_format) (parse_errors @ errors);
2510 let tast = Relative_path.Map.find
tasts path in
2511 Typing_preorder_ser.encode_tys_as_stdout_lines
tast
2513 Stream.iter
process path_stream
2515 let filename = expect_single_file () in
2516 (* Might raise because of Option.value_exn *)
2517 test_decl_compare ctx filename builtins
files_contents files_info
2518 | Shallow_class_diff
->
2519 print_errors_if_present parse_errors;
2520 let filename = expect_single_file () in
2521 test_shallow_class_diff ctx filename
2522 | Get_member class_and_member_id
->
2524 match Str.split
(Str.regexp
"::") class_and_member_id
with
2525 | [cid
; mid
] -> (cid
, mid
)
2528 (Printf.sprintf
"Invalid --get-member ID: %S" class_and_member_id
)
2530 let cid = Utils.add_ns
cid in
2531 (match Decl_provider.get_class
ctx cid with
2532 | None
-> Printf.printf
"No class named %s\n" cid
2534 let ty_to_string ty
=
2536 Typing_env_types.empty
ctx Relative_path.default ~droot
:None
2538 Typing_print.full_strip_ns_decl
env ty
2540 let print_class_element member_type get mid
=
2541 match get cls mid
with
2545 if Typing_defs.get_ce_abstract ce
then
2550 let origin = ce
.Typing_defs.ce_origin
in
2552 if String.equal
origin cid then
2555 Printf.sprintf
" from %s" (Utils.strip_ns
origin)
2562 (ty_to_string (Lazy.force ce
.Typing_defs.ce_type
))
2564 Printf.printf
"%s::%s\n" cid mid
;
2565 print_class_element "method" Cls.get_method mid
;
2566 print_class_element "static method" Cls.get_smethod mid
;
2567 print_class_element "property" Cls.get_prop mid
;
2568 print_class_element "static property" Cls.get_sprop mid
;
2569 print_class_element "static property" Cls.get_sprop
("$" ^ mid
);
2570 (match Cls.get_const cls mid
with
2575 match cc
.cc_abstract
with
2576 | CCAbstract
_ -> "abstract "
2579 let origin = cc
.Typing_defs.cc_origin
in
2581 if String.equal
origin cid then
2584 Printf.sprintf
" from %s" (Utils.strip_ns
origin)
2586 let ty = ty_to_string cc
.Typing_defs.cc_type
in
2587 Printf.printf
" %sconst%s: %s\n" abstract from ty);
2588 (match Cls.get_typeconst cls mid
with
2591 let origin = ttc
.Typing_defs.ttc_origin
in
2593 if String.equal
origin cid then
2596 Printf.sprintf
" from %s" (Utils.strip_ns
origin)
2599 let open Typing_defs
in
2600 match ttc
.ttc_kind
with
2601 | TCConcrete
{ tc_type
= ty } -> "= " ^
ty_to_string ty
2604 atc_as_constraint
= as_cstr
;
2605 atc_super_constraint
= _;
2606 atc_default
= default
;
2612 Option.map as_cstr ~f
:(fun ty -> "as " ^
ty_to_string ty);
2613 Option.map default ~f
:(fun ty -> "= " ^
ty_to_string ty);
2619 match ttc
.ttc_kind
with
2620 | TCConcrete
_ -> ""
2621 | TCAbstract
_ -> "abstract ")
2623 Printf.printf
" %stypeconst%s: %s %s\n" abstract from mid
ty);
2625 | Hover pos_given
->
2626 let filename = expect_single_file () in
2628 Provider_context.add_entry_if_missing ~
ctx ~
path:filename
2630 let (line, column
) =
2631 match pos_given
with
2632 | Some
(line, column
) -> (line, column
)
2634 let src = Provider_context.read_file_contents_exn entry
in
2635 hover_at_caret_pos src
2637 let results = ServerHover.go_quarantined ~
ctx ~entry ~
line ~column
in
2638 let formatted_results =
2641 let open HoverService
in
2642 String.concat ~sep
:"\n" (r
.snippet
:: r
.addendum
))
2647 (String.concat ~sep
:"\n-------------\n" formatted_results)
2648 | Apply_quickfixes
->
2649 let path = expect_single_file () in
2650 let (ctx, entry
) = Provider_context.add_entry_if_missing ~
ctx ~
path in
2652 compute_tasts ~drop_fixmed
:false ctx files_info files_contents
2654 let src = Relative_path.Map.find
files_contents path in
2657 Errors.get_error_list ~drop_fixmed
:false errors
2658 |> List.map ~f
:(fun e ->
2659 (* If an error has multiple possible quickfixes, take the first. *)
2660 List.hd
(User_error.quickfixes e))
2664 let cst = Ast_provider.compute_cst ~
ctx ~entry
in
2665 let tree = Provider_context.PositionedSyntaxTree.root cst in
2667 let classish_starts =
2668 match entry
.Provider_context.source_text
with
2669 | Some source_text
->
2670 Quickfix_ffp.classish_starts
2673 entry
.Provider_context.path
2674 | None
-> SMap.empty
2677 (* Print the title of each quickfix, so we can see text changes in tests. *)
2678 List.iter
quickfixes ~f
:(fun qf
->
2679 Printf.printf
"%s\n" (Quickfix.get_title qf
));
2681 (* Print the source code after applying all these quickfixes. *)
2682 Printf.printf
"\n%s" (Quickfix.apply_all
src classish_starts quickfixes)
2683 | CountImpreciseTypes
->
2684 let (errors, tasts, _gi_solved
) =
2685 compute_tasts_expand_types ctx ~
verbosity files_info files_contents
2687 if not
@@ Errors.is_empty
errors then (
2688 print_errors error_format errors max_errors;
2690 "Did not count imprecise types because there are typing errors.";
2693 let tasts = Relative_path.Map.values
tasts in
2695 List.map ~f
:(Count_imprecise_types.count
ctx) tasts
2697 ~f
:(SMap.union ~combine
:(fun id
_ -> failwith
("Clash at " ^ id
)))
2700 let json = Count_imprecise_types.json_of_results
results in
2701 Printf.printf
"%s" (Hh_json.json_to_string
json)
2703 (*****************************************************************************)
2704 (* Main entry point *)
2705 (*****************************************************************************)
2707 let decl_and_run_mode
2719 should_print_position
;
2721 profile_type_check_multi;
2724 rust_provider_backend;
2726 (popt : TypecheckerOptions.t
)
2728 (naming_table_path
: string option) : unit =
2729 Ident.track_names
:= true;
2732 Relative_path.Map.empty
2734 let extra_builtins =
2735 let add_file_content map
filename =
2736 Relative_path.create
Relative_path.Dummy
filename
2737 |> Multifile.file_to_file_list
2738 |> List.map ~f
:(fun (path, contents) ->
2739 (Filename.basename
(Relative_path.suffix
path), contents))
2740 |> List.unordered_append map
2743 |> List.fold ~f
:add_file_content ~init
:[]
2746 let magic_builtins =
2747 if pessimise_builtins then
2748 pessimised_magic_builtins
2752 let magic_builtins = Array.append
magic_builtins extra_builtins in
2754 match custom_hhi_path with
2755 | None
-> hhi_builtins
2756 | Some
path -> Array.of_list
(Hhi_get.get_hhis_in_dir
path)
2758 (* Check that magic_builtin filenames are unique *)
2760 let n_of_builtins = Array.length
magic_builtins in
2761 let n_of_unique_builtins =
2762 Array.to_list
magic_builtins
2767 if n_of_builtins <> n_of_unique_builtins then
2768 die "Multiple magic builtins share the same base name.\n"
2770 Array.iter
magic_builtins ~f
:(fun (file_name
, file_contents
) ->
2771 let file_path = Path.concat hhi_root file_name
in
2772 let file = Path.to_string
file_path in
2774 (Sys_utils.Touch_existing
{ follow_symlinks
= true })
2776 Sys_utils.write_file ~
file file_contents
);
2778 (* Take the builtins (file, contents) array and create relative paths *)
2780 (Array.append
magic_builtins hhi_builtins)
2781 ~init
:Relative_path.Map.empty
2782 ~f
:(fun acc
(f
, src) ->
2783 let f = Path.concat hhi_root
f |> Path.to_string
in
2784 Relative_path.Map.add
2786 ~key
:(Relative_path.create
Relative_path.Hhi
f)
2790 if use_canonical_filenames () then
2792 |> List.map ~
f:Sys_utils.realpath
2793 |> List.map ~
f:(fun s
-> Option.value_exn s
)
2794 |> List.map ~
f:Relative_path.create_detect_prefix
2796 files |> List.map ~
f:(Relative_path.create
Relative_path.Dummy
)
2798 let files_contents =
2801 ~
f:(fun acc
filename ->
2802 let files_contents = Multifile.file_to_files
filename in
2803 Relative_path.Map.union acc
files_contents)
2804 ~init
:Relative_path.Map.empty
2806 (* Merge in builtins *)
2807 let files_contents_with_builtins =
2808 Relative_path.Map.fold
2812 (fun k
src acc
-> Relative_path.Map.add acc ~key
:k ~data
:src)
2814 ~init
:files_contents
2816 Relative_path.Map.iter
files_contents ~
f:(fun filename contents ->
2817 File_provider.(provide_file_for_tests
filename contents));
2818 (* Don't declare all the filenames in batch_errors mode *)
2823 files_contents_with_builtins
2825 let dbg_deps = Hashtbl.Poly.create
() in
2828 (* In addition to actually recording the dependencies in shared memory,
2829 we build a non-hashed respresentation of the dependency graph
2831 let get_debug_trace root obj
=
2832 let root = Typing_deps.Dep.variant_to_string
root in
2833 let obj = Typing_deps.Dep.variant_to_string
obj in
2834 match Hashtbl.find
dbg_deps obj with
2835 | Some set
-> HashSet.add set
root
2837 let set = HashSet.create
() in
2838 HashSet.add
set root;
2839 Hashtbl.set dbg_deps ~key
:obj ~data
:set
2841 Typing_deps.add_dependency_callback ~name
:"get_debug_trace" get_debug_trace
2843 let dbg_glean_deps = HashSet.create
() in
2845 | Dump_glean_deps
->
2846 (* In addition to actually recording the dependencies in shared memory,
2847 we build a non-hashed respresentation of the dependency graph
2848 for printing. In the callback we receive this as dep_right uses dep_left. *)
2849 let get_debug_trace dep_right dep_left
=
2850 HashSet.add
dbg_glean_deps (dep_left
, dep_right
)
2852 Typing_deps.add_dependency_callback ~name
:"get_debug_trace" get_debug_trace
2855 if rust_provider_backend then (
2856 Provider_backend.set_rust_backend
popt;
2857 Provider_context.empty_for_tool
2860 ~
backend:(Provider_backend.get
())
2861 ~
deps_mode:(Typing_deps_mode.InMemoryMode None
)
2863 Provider_context.empty_for_test
2866 ~
deps_mode:(Typing_deps_mode.InMemoryMode None
)
2868 (* We make the following call for the side-effect of updating ctx's "naming-table fallback"
2869 so it will look in the sqlite database for names it doesn't know.
2870 This function returns the forward naming table. *)
2871 let naming_table_for_root : Naming_table.t
option =
2872 Option.map naming_table_path ~
f:(fun path ->
2873 Naming_table.load_from_sqlite
ctx path)
2875 (* If run in naming-table mode, we first have to remove any old names from the files we're about to redeclare --
2876 otherwise when we declare them it'd count as a duplicate definition! *)
2877 Option.iter
naming_table_for_root ~
f:(fun naming_table_for_root ->
2878 Relative_path.Map.iter
files_contents ~
f:(fun file _content
->
2880 Naming_table.get_file_info
naming_table_for_root file
2882 Option.iter
file_info ~
f:(fun file_info ->
2883 let ids_to_strings ids
=
2884 List.map ids ~
f:(fun (_, name
, _) -> name
)
2886 Naming_global.remove_decls
2887 ~
backend:(Provider_context.get_backend
ctx)
2888 ~funs
:(ids_to_strings file_info.FileInfo.funs
)
2889 ~classes
:(ids_to_strings file_info.FileInfo.classes
)
2890 ~typedefs
:(ids_to_strings file_info.FileInfo.typedefs
)
2891 ~consts
:(ids_to_strings file_info.FileInfo.consts
)
2892 ~modules
:(ids_to_strings file_info.FileInfo.modules
))));
2894 let (errors, files_info) = parse_name_and_decl ctx to_decl in
2902 (Errors.get_sorted_error_list
errors)
2909 ~should_print_position
2910 ~
profile_type_check_multi
2915 ({ tcopt; _ } as opts)
2917 (naming_table : string option)
2918 (sharedmem_config : SharedMem.config
) : unit =
2919 (* TODO: We should have a per file config *)
2920 Sys_utils.signal
Sys.sigusr1
(Sys.Signal_handle
Typing.debug_print_last_pos
);
2921 EventLogger.init_fake
();
2922 Measure.push_global
();
2924 let (_handle
: SharedMem.handle
) =
2925 SharedMem.init ~num_workers
:0 sharedmem_config
2927 let process custom hhi_root
=
2929 let hhi_root_s = Path.to_string hhi_root
in
2930 if Disk.file_exists
hhi_root_s && Disk.is_directory
hhi_root_s then
2931 Hhi.set_custom_hhi_root hhi_root
2933 die ("Custom hhi directory " ^
hhi_root_s ^
" not found")
2935 Hhi.set_hhi_root_for_unit_test hhi_root
;
2936 Relative_path.set_path_prefix
Relative_path.Root
root;
2937 Relative_path.set_path_prefix
Relative_path.Hhi hhi_root
;
2938 Relative_path.set_path_prefix
Relative_path.Tmp
(Path.make
"tmp");
2939 decl_and_run_mode opts tcopt hhi_root
naming_table;
2940 TypingLogger.flush_buffers
()
2942 match opts.custom_hhi_path with
2943 | Some hhi_root
-> process true (Path.make hhi_root
)
2944 | None
-> Tempfile.with_tempdir
(fun hhi_root
-> process false hhi_root
)
2946 (* command line driver *)
2948 if !Sys.interactive
then
2951 (* On windows, setting 'binary mode' avoids to output CRLF on
2952 stdout. The 'text mode' would not hurt the user in general, but
2953 it breaks the testsuite where the output is compared to the
2954 expected one (i.e. in given file without CRLF). *)
2955 Out_channel.set_binary_mode stdout
true;
2956 let (options, root, naming_table, sharedmem_config) = parse_options () in
2957 Unix.handle_unix_error
main_hack options root naming_table sharedmem_config