Move Nast -> decl functions into Decl_nast and Decl_folded_class modules
[hiphop-php.git] / hphp / hack / src / hh_single_type_check.ml
blobfbfb3d317bb39518ebb6367f89d6291d3af595a9
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Core_kernel
11 open File_content
12 open String_utils
13 open Sys_utils
14 open Typing_env_types
15 module Inf = Typing_inference_env
17 module StringAnnotation = struct
18 type t = string
20 let pp fmt str = Format.pp_print_string fmt str
21 end
23 module PS = Full_fidelity_positioned_syntax
24 module PositionedTree = Full_fidelity_syntax_tree.WithSyntax (PS)
26 (*****************************************************************************)
27 (* Types, constants *)
28 (*****************************************************************************)
30 type mode =
31 | Ifc of string * string
32 | Ai of Ai_options.t
33 | Autocomplete
34 | Autocomplete_manually_invoked
35 | Ffp_autocomplete
36 | Color
37 | Coverage
38 | Cst_search
39 | Dump_symbol_info
40 | Dump_inheritance
41 | Errors
42 | Lint
43 | Dump_deps
44 | Identify_symbol of int * int
45 | Find_local of int * int
46 | Outline
47 | Dump_nast
48 | Dump_stripped_tast
49 | Dump_tast
50 | Check_tast
51 | Rewrite
52 | Find_refs of int * int
53 | Highlight_refs of int * int
54 | Decl_compare
55 | Shallow_class_diff
56 | Linearization
57 | Go_to_impl of int * int
58 | Dump_glean_deps
60 type options = {
61 files: string list;
62 mode: mode;
63 error_format: Errors.format;
64 no_builtins: bool;
65 max_errors: int option;
66 tcopt: GlobalOptions.t;
67 batch_mode: bool;
68 out_extension: string;
69 verbosity: int;
72 (* Canonical builtins from our hhi library *)
73 let hhi_builtins = Hhi.get_raw_hhi_contents ()
75 (* All of the stuff that hh_single_type_check relies on is sadly not contained
76 * in the hhi library, so we include a very small number of magic builtins *)
77 let magic_builtins =
79 ( "hh_single_type_check_magic.hhi",
80 "<?hh\n"
81 ^ "namespace {\n"
82 ^ "async function gena<Tk as arraykey, Tv>(
83 KeyedTraversable<Tk, Awaitable<Tv>> $awaitables,
84 ): Awaitable<darray<Tk, Tv>>;\n"
85 ^ "function hh_show(<<__AcceptDisposable>> $val) {}\n"
86 ^ "function hh_show_env() {}\n"
87 ^ "function hh_log_level($key, $level) {}\n"
88 ^ "function hh_force_solve () {}"
89 ^ "}\n"
90 ^ "namespace HH\\Lib\\Tuple{\n"
91 ^ "function gen();\n"
92 ^ "function from_async();\n"
93 ^ "}\n" );
96 (*****************************************************************************)
97 (* Helpers *)
98 (*****************************************************************************)
100 let die str =
101 let oc = stderr in
102 Out_channel.output_string oc str;
103 Out_channel.close oc;
104 exit 2
106 let print_error format ?(oc = stderr) l =
107 let formatter =
108 match format with
109 | Errors.Context -> (fun e -> Contextual_error_formatter.to_string e)
110 | Errors.Raw -> (fun e -> Errors.to_string e)
111 | Errors.Highlighted -> Highlighted_error_formatter.to_string
113 Out_channel.output_string oc (formatter (Errors.to_absolute_for_test l))
115 let write_error_list format errors oc max_errors =
116 let (shown_errors, dropped_errors) =
117 match max_errors with
118 | Some max_errors -> List.split_n errors max_errors
119 | None -> (errors, [])
121 if errors <> [] then (
122 List.iter ~f:(print_error format ~oc) shown_errors;
123 match
124 Errors.format_summary
125 format
126 errors
127 (List.length dropped_errors)
128 max_errors
129 with
130 | Some summary -> Out_channel.output_string oc summary
131 | None -> ()
132 ) else
133 Out_channel.output_string oc "No errors\n";
134 Out_channel.close oc
136 let print_error_list format errors max_errors =
137 let (shown_errors, dropped_errors) =
138 match max_errors with
139 | Some max_errors -> List.split_n errors max_errors
140 | None -> (errors, [])
142 if errors <> [] then (
143 List.iter ~f:(print_error format) shown_errors;
144 match
145 Errors.format_summary
146 format
147 errors
148 (List.length dropped_errors)
149 max_errors
150 with
151 | Some summary -> Out_channel.output_string stderr summary
152 | None -> ()
153 ) else
154 Printf.printf "No errors\n"
156 let print_errors format (errors : Errors.t) max_errors : unit =
157 print_error_list format (Errors.get_sorted_error_list errors) max_errors
159 let print_errors_if_present (errors : Errors.error list) =
160 if not (List.is_empty errors) then (
161 let errors_output = Errors.convert_errors_to_string errors in
162 Printf.printf "Errors:\n";
163 List.iter errors_output (fun err_output ->
164 Printf.printf " %s\n" err_output)
167 let parse_options () =
168 let fn_ref = ref [] in
169 let usage = Printf.sprintf "Usage: %s filename\n" Sys.argv.(0) in
170 let mode = ref Errors in
171 let no_builtins = ref false in
172 let line = ref 0 in
173 let log_key = ref "" in
174 let log_levels = ref SMap.empty in
175 let max_errors = ref None in
176 let batch_mode = ref false in
177 let set_mode x () =
178 if !mode <> Errors then
179 raise (Arg.Bad "only a single mode should be specified")
180 else
181 mode := x
183 let ifc_mode = ref "" in
184 let set_ifc lattice = set_mode (Ifc (!ifc_mode, lattice)) () in
185 let set_ai x = set_mode (Ai (Ai_options.prepare ~server:false x)) () in
186 let error_format = ref Errors.Highlighted in
187 let forbid_nullable_cast = ref false in
188 let deregister_attributes = ref None in
189 let disallow_array_typehint = ref None in
190 let disallow_array_literal = ref None in
191 let dynamic_view = ref None in
192 let auto_namespace_map = ref None in
193 let unsafe_rx = ref (Some false) in
194 let log_inference_constraints = ref None in
195 let timeout = ref None in
196 let disallow_byref_dynamic_calls = ref (Some false) in
197 let disallow_byref_calls = ref (Some false) in
198 let set_bool x () = x := Some true in
199 let set_bool_ x () = x := true in
200 let set_float_ x f = x := f in
201 let shallow_class_decl = ref false in
202 let out_extension = ref ".out" in
203 let like_type_hints = ref false in
204 let union_intersection_type_hints = ref false in
205 let coeffects = ref false in
206 let like_casts = ref false in
207 let simple_pessimize = ref 0.0 in
208 let complex_coercion = ref false in
209 let disable_partially_abstract_typeconsts = ref false in
210 let rust_parser_errors = ref false in
211 let symbolindex_file = ref None in
212 let check_xhp_attribute = ref false in
213 let check_redundant_generics = ref false in
214 let disallow_invalid_arraykey_constraint = ref None in
215 let enable_class_level_where_clauses = ref false in
216 let disallow_trait_reuse = ref None in
217 let disable_legacy_soft_typehints = ref false in
218 let allow_new_attribute_syntax = ref false in
219 let allow_toplevel_requires = ref false in
220 let global_inference = ref false in
221 let ordered_solving = ref false in
222 let reinfer_types = ref [] in
223 let const_static_props = ref false in
224 let disable_legacy_attribute_syntax = ref false in
225 let const_attribute = ref false in
226 let disallow_goto = ref false in
227 let const_default_func_args = ref false in
228 let const_default_lambda_args = ref false in
229 let disallow_silence = ref false in
230 let abstract_static_props = ref false in
231 let disable_unset_class_const = ref false in
232 let glean_service = ref (GleanOptions.service GlobalOptions.default) in
233 let glean_hostname = ref (GleanOptions.hostname GlobalOptions.default) in
234 let glean_port = ref (GleanOptions.port GlobalOptions.default) in
235 let glean_reponame = ref (GleanOptions.reponame GlobalOptions.default) in
236 let disallow_func_ptrs_in_constants = ref false in
237 let error_php_lambdas = ref false in
238 let disallow_discarded_nullable_awaitables = ref false in
239 let disable_xhp_element_mangling = ref false in
240 let disable_xhp_children_declarations = ref false in
241 let enable_xhp_class_modifier = ref false in
242 let verbosity = ref 0 in
243 let enable_first_class_function_pointers = ref false in
244 let disable_modes = ref false in
245 let disable_hh_ignore_error = ref false in
246 let enable_systemlib_annotations = ref false in
247 let enable_pocket_universes_syntax = ref false in
248 let enable_higher_kinded_types = ref false in
249 let allowed_fixme_codes_strict = ref ISet.empty in
250 let allowed_fixme_codes_partial = ref ISet.empty in
251 let codes_not_raised_partial = ref ISet.empty in
252 let allowed_decl_fixme_codes = ref ISet.empty in
253 let method_call_inference = ref false in
254 let report_pos_from_reason = ref false in
255 let options =
257 ( "--ifc",
258 Arg.Tuple [Arg.String (fun m -> ifc_mode := m); Arg.String set_ifc],
259 " Run the flow analysis" );
260 ("--ai", Arg.String set_ai, " Run the abstract interpreter (Zoncolan)");
261 ( "--deregister-attributes",
262 Arg.Unit (set_bool deregister_attributes),
263 " Ignore all functions with attribute '__PHPStdLib'" );
264 ( "--auto-complete",
265 Arg.Unit (set_mode Autocomplete),
266 " Produce autocomplete suggestions as if triggered by trigger character"
268 ( "--auto-complete-manually-invoked",
269 Arg.Unit (set_mode Autocomplete_manually_invoked),
270 " Produce autocomplete suggestions as if manually triggered by user" );
271 ( "--auto-namespace-map",
272 Arg.String
273 (fun m ->
274 auto_namespace_map :=
275 Some (ServerConfig.convert_auto_namespace_to_map m)),
276 " Alias namespaces" );
277 ( "--ffp-auto-complete",
278 Arg.Unit (set_mode Ffp_autocomplete),
279 " Produce autocomplete suggestions using the full-fidelity parse tree"
281 ("--colour", Arg.Unit (set_mode Color), " Produce colour output");
282 ("--color", Arg.Unit (set_mode Color), " Produce color output");
283 ("--coverage", Arg.Unit (set_mode Coverage), " Produce coverage output");
284 ( "--cst-search",
285 Arg.Unit (set_mode Cst_search),
286 " Search the concrete syntax tree of the given file using the pattern"
287 ^ " given on stdin."
288 ^ " (The pattern is a JSON object adhering to the search DSL.)" );
289 ( "--dump-symbol-info",
290 Arg.Unit (set_mode Dump_symbol_info),
291 " Dump all symbol information" );
292 ( "--error-format",
293 Arg.String
294 (fun s ->
295 match s with
296 | "raw" -> error_format := Errors.Raw
297 | "context" -> error_format := Errors.Context
298 | "highlighted" -> error_format := Errors.Highlighted
299 | _ -> print_string "Warning: unrecognized error format.\n"),
300 "<raw|context|highlighted> Error formatting style" );
301 ("--lint", Arg.Unit (set_mode Lint), " Produce lint errors");
302 ( "--no-builtins",
303 Arg.Set no_builtins,
304 " Don't use builtins (e.g. ConstSet)" );
305 ( "--out-extension",
306 Arg.String (fun s -> out_extension := s),
307 "output file extension (default .out)" );
308 ("--dump-deps", Arg.Unit (set_mode Dump_deps), " Print dependencies");
309 ( "--dump-glean-deps",
310 Arg.Unit (set_mode Dump_glean_deps),
311 " Print dependencies in the Glean format" );
312 ( "--dump-inheritance",
313 Arg.Unit (set_mode Dump_inheritance),
314 " Print inheritance" );
315 ( "--identify-symbol",
316 Arg.Tuple
318 Arg.Int (fun x -> line := x);
319 Arg.Int
320 (fun column -> set_mode (Identify_symbol (!line, column)) ());
322 "<pos> Show info about symbol at given line and column" );
323 ( "--find-local",
324 Arg.Tuple
326 Arg.Int (fun x -> line := x);
327 Arg.Int (fun column -> set_mode (Find_local (!line, column)) ());
329 "<pos> Find all usages of local at given line and column" );
330 ( "--max-errors",
331 Arg.Int (fun num_errors -> max_errors := Some num_errors),
332 " Maximum number of errors to display" );
333 ("--outline", Arg.Unit (set_mode Outline), " Print file outline");
334 ("--nast", Arg.Unit (set_mode Dump_nast), " Print out the named AST");
335 ("--tast", Arg.Unit (set_mode Dump_tast), " Print out the typed AST");
336 ("--tast-check", Arg.Unit (set_mode Check_tast), " Typecheck the tast");
337 ( "--stripped-tast",
338 Arg.Unit (set_mode Dump_stripped_tast),
339 " Print out the typed AST, stripped of type information."
340 ^ " This can be compared against the named AST to look for holes." );
341 ( "--rewrite",
342 Arg.Unit (set_mode Rewrite),
343 " Rewrite the file after inferring types using global inference"
344 ^ " (requires --global-inference)." );
345 ( "--global-inference",
346 Arg.Set global_inference,
347 "Global type inference to infer missing type annotations." );
348 ( "--ordered-solving",
349 Arg.Set ordered_solving,
350 "Optimized solver for type variables. Experimental." );
351 ( "--reinfer-types",
352 Arg.String (fun s -> reinfer_types := Str.split (Str.regexp ", *") s),
353 "List of type hint to be ignored and infered again using global inference."
355 ( "--find-refs",
356 Arg.Tuple
358 Arg.Int (fun x -> line := x);
359 Arg.Int (fun column -> set_mode (Find_refs (!line, column)) ());
361 "<pos> Find all usages of a symbol at given line and column" );
362 ( "--go-to-impl",
363 Arg.Tuple
365 Arg.Int (fun x -> line := x);
366 Arg.Int (fun column -> set_mode (Go_to_impl (!line, column)) ());
368 "<pos> Find all implementations of a symbol at given line and column" );
369 ( "--highlight-refs",
370 Arg.Tuple
372 Arg.Int (fun x -> line := x);
373 Arg.Int (fun column -> set_mode (Highlight_refs (!line, column)) ());
375 "<pos> Highlight all usages of a symbol at given line and column" );
376 ( "--decl-compare",
377 Arg.Unit (set_mode Decl_compare),
378 " Test comparison functions used in incremental mode on declarations"
379 ^ " in provided file" );
380 ( "--shallow-class-diff",
381 Arg.Unit (set_mode Shallow_class_diff),
382 " Test shallow class comparison used in incremental mode on shallow class declarations"
384 ( "--forbid_nullable_cast",
385 Arg.Set forbid_nullable_cast,
386 " Forbid casting from nullable values." );
387 ( "--disallow-array-typehint",
388 Arg.Unit (set_bool disallow_array_typehint),
389 " Disallow usage of array typehints." );
390 ( "--disallow-array-literal",
391 Arg.Unit (set_bool disallow_array_literal),
392 " Disallow usage of array literals." );
393 ( "--dynamic-view",
394 Arg.Unit (set_bool dynamic_view),
395 " Turns on dynamic view, replacing Tany with dynamic" );
396 ( "--unsafe-rx",
397 Arg.Unit (set_bool unsafe_rx),
398 " Disables reactivity related errors" );
399 ( "--mro",
400 Arg.Unit (set_mode Linearization),
401 " Grabs the linearization of all classes in a file." );
402 ( "--log-inference-constraints",
403 Arg.Unit (set_bool log_inference_constraints),
404 " Log inference constraints to Scuba." );
405 ( "--timeout",
406 Arg.Int (fun secs -> timeout := Some secs),
407 " Timeout in seconds for checking a function or a class." );
408 ( "--hh-log-level",
409 Arg.Tuple
411 Arg.String (fun x -> log_key := x);
412 Arg.Int
413 (fun level -> log_levels := SMap.add !log_key level !log_levels);
415 " Set the log level for a key" );
416 ( "--batch-files",
417 Arg.Set batch_mode,
418 " Typecheck each file passed in independently" );
419 ( "--disallow-invalid-arraykey-constraint",
420 Arg.Unit (set_bool disallow_invalid_arraykey_constraint),
421 " Disallow using non-string, non-int types as array key constraints" );
422 ( "--check-xhp-attribute",
423 Arg.Set check_xhp_attribute,
424 " Typechecks xhp required attributes" );
425 ( "--disallow-byref-dynamic-calls",
426 Arg.Unit (set_bool disallow_byref_dynamic_calls),
427 " Disallow passing arguments by reference to dynamically called functions [e.g. $foo(&$bar)]"
429 ( "--disallow-byref-calls",
430 Arg.Unit (set_bool disallow_byref_calls),
431 " Disallow passing arguments by reference in any form [e.g. foo(&$bar)]"
433 ( "--shallow-class-decl",
434 Arg.Set shallow_class_decl,
435 " Look up class members lazily from shallow declarations" );
436 ( "--union-intersection-type-hints",
437 Arg.Set union_intersection_type_hints,
438 " Allows union and intersection types to be written in type hint positions"
440 ( "--like-type-hints",
441 Arg.Set like_type_hints,
442 " Allows like types to be written in type hint positions" );
443 ( "--like-casts",
444 Arg.Set like_casts,
445 " Allows like types to be written in as expressions" );
446 ( "--simple-pessimize",
447 Arg.Set_float simple_pessimize,
448 " At coercion points, if a type is not enforceable, wrap it in like. Float argument 0.0 to 1.0 sets frequency"
450 ( "--complex-coercion",
451 Arg.Set complex_coercion,
452 " Allows complex coercions that involve like types" );
453 ( "--like-types-all",
454 Arg.Unit
455 (fun () ->
456 set_bool_ like_type_hints ();
457 set_bool_ like_casts ();
458 set_float_ simple_pessimize 1.0;
459 set_bool_ complex_coercion ()),
460 " Enables all like types features" );
461 ( "--disable-partially-abstract-typeconsts",
462 Arg.Set disable_partially_abstract_typeconsts,
463 " Treat partially abstract type constants as concrete type constants" );
464 ( "--rust-parser-errors",
465 Arg.Bool (fun x -> rust_parser_errors := x),
466 " Use rust parser error checker" );
467 ( "--symbolindex-file",
468 Arg.String (fun str -> symbolindex_file := Some str),
469 " Load the symbol index from this file" );
470 ( "--enable-class-level-where-clauses",
471 Arg.Set enable_class_level_where_clauses,
472 "Enables support for class-level where clauses" );
473 ( "--disallow-trait-reuse",
474 Arg.Unit (set_bool disallow_trait_reuse),
475 " Forbid a class from using a trait already used in a parent class" );
476 ( "--disable-legacy-soft-typehints",
477 Arg.Set disable_legacy_soft_typehints,
478 " Disables the legacy @ syntax for soft typehints (use __Soft instead)"
480 ( "--allow-new-attribute-syntax",
481 Arg.Set allow_new_attribute_syntax,
482 " Allow the new @ attribute syntax (disables legacy soft typehints)" );
483 ( "--allow-toplevel-requires",
484 Arg.Set allow_toplevel_requires,
485 " Allow `require()` and similar at the top-level" );
486 ( "--const-static-props",
487 Arg.Set const_static_props,
488 " Enable static properties to be const" );
489 ( "--disable-legacy-attribute-syntax",
490 Arg.Set disable_legacy_attribute_syntax,
491 " Disable the legacy <<...>> user attribute syntax" );
492 ("--const-attribute", Arg.Set const_attribute, " Allow __Const attribute");
493 ( "--disallow-goto",
494 Arg.Set disallow_goto,
495 " Forbid the goto operator and goto labels in the parser" );
496 ( "--const-default-func-args",
497 Arg.Set const_default_func_args,
498 " Statically check default function arguments are constant initializers"
500 ( "--const-default-lambda-args",
501 Arg.Set const_default_lambda_args,
502 " Statically check default lambda args are constant."
503 ^ " Produces a subset of errors of const-default-func-args" );
504 ( "--disallow-silence",
505 Arg.Set disallow_silence,
506 " Disallow the error suppression operator, @" );
507 ( "--abstract-static-props",
508 Arg.Set abstract_static_props,
509 " Static properties can be abstract" );
510 ( "--disable-unset-class-const",
511 Arg.Set disable_unset_class_const,
512 " Make unsetting a class const a parse error" );
513 ( "--glean-service",
514 Arg.String (fun str -> glean_service := str),
515 " glean service name" );
516 ( "--glean-hostname",
517 Arg.String (fun str -> glean_hostname := str),
518 " glean hostname" );
519 ("--glean-port", Arg.Int (fun x -> glean_port := x), " glean port number");
520 ( "--glean-reponame",
521 Arg.String (fun str -> glean_reponame := str),
522 "glean repo name" );
523 ( "--disallow-func-ptrs-in-constants",
524 Arg.Set disallow_func_ptrs_in_constants,
525 " Disallow use of HH\\fun and HH\\class_meth in constants and constant initializers"
527 ( "--disallow-php-lambdas",
528 Arg.Set error_php_lambdas,
529 "Disallow php style anonymous functions." );
530 ( "--disallow-discarded-nullable-awaitables",
531 Arg.Set disallow_discarded_nullable_awaitables,
532 "Error on using discarded nullable awaitables" );
533 ( "--disable-xhp-element-mangling",
534 Arg.Set disable_xhp_element_mangling,
535 "Disable mangling of XHP elements :foo. That is, :foo:bar is now \\foo\\bar, not xhp_foo__bar"
537 ( "--disable-xhp-children-declarations",
538 Arg.Set disable_xhp_children_declarations,
539 "Disable XHP children declarations, e.g. children (foo, bar+)" );
540 ( "--enable-xhp-class-modifier",
541 Arg.Set enable_xhp_class_modifier,
542 "Enable the XHP class modifier, xhp class name {} will define an xhp class."
544 ( "--verbose",
545 Arg.Int (fun v -> verbosity := v),
546 "Verbosity as an integer." );
547 ( "--enable-first-class-function-pointers",
548 Arg.Set enable_first_class_function_pointers,
549 "Enable first class funciton pointers using <> syntax" );
550 ("--disable-modes", Arg.Set disable_modes, "Treat partial as strict");
551 ( "--disable-hh-ignore-error",
552 Arg.Set disable_hh_ignore_error,
553 "Treat HH_IGNORE_ERROR comments as normal comments" );
554 ( "--enable-systemlib-annotations",
555 Arg.Set enable_systemlib_annotations,
556 "Enable systemlib annotations" );
557 ( "--enable-pocket-universes-syntax",
558 Arg.Set enable_pocket_universes_syntax,
559 "Enable the pocket universes syntax" );
560 ( "--enable-higher-kinded-types",
561 Arg.Set enable_higher_kinded_types,
562 "Enable support for higher-kinded types" );
563 ( "--allowed-fixme-codes-strict",
564 Arg.String
565 (fun s ->
566 allowed_fixme_codes_strict :=
567 Str.split (Str.regexp ", *") s
568 |> List.map ~f:int_of_string
569 |> ISet.of_list),
570 "List of fixmes that are allowed in strict mode." );
571 ( "--allowed-fixme-codes-partial",
572 Arg.String
573 (fun s ->
574 allowed_fixme_codes_partial :=
575 Str.split (Str.regexp ", *") s
576 |> List.map ~f:int_of_string
577 |> ISet.of_list),
578 "List of fixmes that are allowed in partial mode." );
579 ( "--codes-not-raised-partial",
580 Arg.String
581 (fun s ->
582 codes_not_raised_partial :=
583 Str.split (Str.regexp ", *") s
584 |> List.map ~f:int_of_string
585 |> ISet.of_list),
586 "List of error codes that are not raised in partial mode." );
587 ( "--allowed-decl-fixme-codes",
588 Arg.String
589 (fun s ->
590 allowed_decl_fixme_codes :=
591 Str.split (Str.regexp ", *") s
592 |> List.map ~f:int_of_string
593 |> ISet.of_list),
594 "List of fixmes that are allowed in declarations." );
595 ( "--method-call-inference",
596 Arg.Set method_call_inference,
597 " Infer constraints for method calls. NB: incompatible with like types."
599 ( "--report-pos-from-reason",
600 Arg.Set report_pos_from_reason,
601 " Flag errors whose position is derived from reason information in types."
605 let options = Arg.align ~limit:25 options in
606 Arg.parse options (fun fn -> fn_ref := fn :: !fn_ref) usage;
607 let fns =
608 match !fn_ref with
609 | [] -> die usage
610 | x -> x
612 let tcopt =
613 GlobalOptions.make
614 ?po_disable_array_typehint:(Some false)
615 ?tco_unsafe_rx:!unsafe_rx
616 ?po_deregister_php_stdlib:!deregister_attributes
617 ?tco_disallow_array_typehint:!disallow_array_typehint
618 ?tco_disallow_array_literal:!disallow_array_literal
619 ?tco_dynamic_view:!dynamic_view
620 ?tco_log_inference_constraints:!log_inference_constraints
621 ?tco_timeout:!timeout
622 ?po_auto_namespace_map:!auto_namespace_map
623 ?tco_disallow_byref_dynamic_calls:!disallow_byref_dynamic_calls
624 ?tco_disallow_byref_calls:!disallow_byref_calls
625 ~allowed_fixme_codes_strict:!allowed_fixme_codes_strict
626 ~allowed_fixme_codes_partial:!allowed_fixme_codes_partial
627 ~codes_not_raised_partial:!codes_not_raised_partial
628 ?tco_disallow_invalid_arraykey_constraint:
629 !disallow_invalid_arraykey_constraint
630 ?tco_disallow_trait_reuse:!disallow_trait_reuse
631 ~tco_check_xhp_attribute:!check_xhp_attribute
632 ~tco_check_redundant_generics:!check_redundant_generics
633 ~tco_shallow_class_decl:!shallow_class_decl
634 ~tco_like_type_hints:!like_type_hints
635 ~tco_union_intersection_type_hints:!union_intersection_type_hints
636 ~tco_coeffects:!coeffects
637 ~tco_like_casts:!like_casts
638 ~tco_simple_pessimize:!simple_pessimize
639 ~tco_complex_coercion:!complex_coercion
640 ~tco_disable_partially_abstract_typeconsts:
641 !disable_partially_abstract_typeconsts
642 ~log_levels:!log_levels
643 ~po_rust_parser_errors:!rust_parser_errors
644 ~po_enable_class_level_where_clauses:!enable_class_level_where_clauses
645 ~po_disable_legacy_soft_typehints:!disable_legacy_soft_typehints
646 ~po_allow_new_attribute_syntax:!allow_new_attribute_syntax
647 ~po_disallow_toplevel_requires:(not !allow_toplevel_requires)
648 ~tco_const_static_props:!const_static_props
649 ~tco_global_inference:!global_inference
650 ~tco_ordered_solving:!ordered_solving
651 ~tco_gi_reinfer_types:!reinfer_types
652 ~po_disable_legacy_attribute_syntax:!disable_legacy_attribute_syntax
653 ~tco_const_attribute:!const_attribute
654 ~po_allow_goto:(not !disallow_goto)
655 ~po_const_default_func_args:!const_default_func_args
656 ~po_const_default_lambda_args:!const_default_lambda_args
657 ~po_disallow_silence:!disallow_silence
658 ~po_abstract_static_props:!abstract_static_props
659 ~po_disable_unset_class_const:!disable_unset_class_const
660 ~po_disallow_func_ptrs_in_constants:!disallow_func_ptrs_in_constants
661 ~tco_check_attribute_locations:true
662 ~tco_error_php_lambdas:!error_php_lambdas
663 ~tco_disallow_discarded_nullable_awaitables:
664 !disallow_discarded_nullable_awaitables
665 ~glean_service:!glean_service
666 ~glean_hostname:!glean_hostname
667 ~glean_port:!glean_port
668 ~glean_reponame:!glean_reponame
669 ~po_disable_xhp_element_mangling:!disable_xhp_element_mangling
670 ~po_disable_xhp_children_declarations:!disable_xhp_children_declarations
671 ~po_enable_xhp_class_modifier:!enable_xhp_class_modifier
672 ~po_enable_first_class_function_pointers:
673 !enable_first_class_function_pointers
674 ~po_disable_modes:!disable_modes
675 ~po_disable_hh_ignore_error:!disable_hh_ignore_error
676 ~tco_enable_systemlib_annotations:!enable_systemlib_annotations
677 ~tco_higher_kinded_types:!enable_higher_kinded_types
678 ~po_allowed_decl_fixme_codes:!allowed_decl_fixme_codes
679 ~po_allow_unstable_features:true
680 ~tco_method_call_inference:!method_call_inference
681 ~tco_report_pos_from_reason:!report_pos_from_reason
684 Errors.allowed_fixme_codes_strict :=
685 GlobalOptions.allowed_fixme_codes_strict tcopt;
686 Errors.allowed_fixme_codes_partial :=
687 GlobalOptions.allowed_fixme_codes_partial tcopt;
688 Errors.codes_not_raised_partial :=
689 GlobalOptions.codes_not_raised_partial tcopt;
690 Errors.report_pos_from_reason :=
691 GlobalOptions.tco_report_pos_from_reason tcopt;
692 let tcopt =
694 tcopt with
695 GlobalOptions.tco_experimental_features =
696 SSet.filter
697 begin
698 fun x ->
699 if x = GlobalOptions.tco_experimental_forbid_nullable_cast then
700 !forbid_nullable_cast
701 else
702 true
704 tcopt.GlobalOptions.tco_experimental_features;
707 (* Configure symbol index settings *)
708 let namespace_map = GlobalOptions.po_auto_namespace_map tcopt in
709 let sienv =
710 SymbolIndex.initialize
711 ~globalrev:None
712 ~gleanopt:tcopt
713 ~namespace_map
714 ~provider_name:"LocalIndex"
715 ~quiet:true
716 ~ignore_hh_version:false
717 ~savedstate_file_opt:!symbolindex_file
718 ~workers:None
720 let sienv =
722 sienv with
723 SearchUtils.sie_resolve_signatures = true;
724 SearchUtils.sie_resolve_positions = true;
725 SearchUtils.sie_resolve_local_decl = true;
729 files = fns;
730 mode = !mode;
731 no_builtins = !no_builtins;
732 max_errors = !max_errors;
733 error_format = !error_format;
734 tcopt;
735 batch_mode = !batch_mode;
736 out_extension = !out_extension;
737 verbosity = !verbosity;
739 sienv )
741 (* Make readable test output *)
742 let replace_color input =
743 Ide_api_types.(
744 match input with
745 | (Some Unchecked, str) -> "<unchecked>" ^ str ^ "</unchecked>"
746 | (Some Checked, str) -> "<checked>" ^ str ^ "</checked>"
747 | (Some Partial, str) -> "<partial>" ^ str ^ "</partial>"
748 | (None, str) -> str)
750 let print_colored fn type_acc =
751 let content = cat (Relative_path.to_absolute fn) in
752 let results = ColorFile.go content type_acc in
753 if Unix.isatty Unix.stdout then
754 Tty.cprint (ClientColorFile.replace_colors results)
755 else
756 print_string (List.map ~f:replace_color results |> String.concat ~sep:"")
758 let print_coverage type_acc =
759 ClientCoverageMetric.go ~json:false (Some (Coverage_level_defs.Leaf type_acc))
761 let print_global_inference_envs ctx ~verbosity gienvs =
762 let gienvs =
763 Typing_global_inference.StateSubConstraintGraphs.global_tvenvs gienvs
765 let tco_global_inference =
766 TypecheckerOptions.global_inference (Provider_context.get_tcopt ctx)
768 if verbosity >= 2 && tco_global_inference then
769 let should_log (pos, gienv) =
770 let file_relevant =
771 match verbosity with
773 when Filename.check_suffix
774 (Relative_path.suffix (Pos.filename pos))
775 ".hhi" ->
776 false
777 | _ -> true
779 file_relevant && (not @@ List.is_empty @@ Inf.get_vars_g gienv)
781 let env = Typing_env.empty ctx Relative_path.default ~droot:None in
783 List.filter gienvs ~f:should_log
784 |> List.iter ~f:(fun (pos, gienv) ->
785 Typing_log.log_global_inference_env pos env gienv)
787 let merge_global_inference_envs_opt ctx gienvs :
788 Typing_global_inference.StateConstraintGraph.t option =
789 if TypecheckerOptions.global_inference (Provider_context.get_tcopt ctx) then
790 let open Typing_global_inference in
791 let (type_map, env, state_errors) =
792 StateConstraintGraph.merge_subgraphs ctx [gienvs]
794 (* we are not going to print type variables without any bounds *)
795 let env = { env with inference_env = Inf.compress env.inference_env } in
796 Some (type_map, env, state_errors)
797 else
798 None
800 let print_global_inference_env
801 env ~step_name state_errors error_format max_errors =
802 let print_header s =
803 print_endline "";
804 print_endline (String.map s (const '='));
805 print_endline s;
806 print_endline (String.map s (const '='))
808 print_header (Printf.sprintf "%sd environment" step_name);
809 Typing_log.hh_show_full_env Pos.none env;
811 print_header (Printf.sprintf "%s errors" step_name);
812 List.iter
813 (Typing_global_inference.StateErrors.elements state_errors)
814 ~f:(fun (var, errl) ->
815 Printf.fprintf stderr "#%d\n" var;
816 print_error_list error_format errl max_errors);
817 Out_channel.flush stderr
819 let print_merged_global_inference_env
820 ~verbosity
821 (gienv : Typing_global_inference.StateConstraintGraph.t option)
822 error_format
823 max_errors =
824 if verbosity >= 1 then
825 match gienv with
826 | None -> ()
827 | Some (_type_map, gienv, state_errors) ->
828 print_global_inference_env
829 gienv
830 ~step_name:"Merge"
831 state_errors
832 error_format
833 max_errors
835 let print_solved_global_inference_env
836 ~verbosity
837 (gienv : Typing_global_inference.StateSolvedGraph.t option)
838 error_format
839 max_errors =
840 if verbosity >= 1 then
841 match gienv with
842 | None -> ()
843 | Some (gienv, state_errors, _type_map) ->
844 print_global_inference_env
845 gienv
846 ~step_name:"Solve"
847 state_errors
848 error_format
849 max_errors
851 let solve_global_inference_env
852 (gienv : Typing_global_inference.StateConstraintGraph.t) :
853 Typing_global_inference.StateSolvedGraph.t =
854 Typing_global_inference.StateSolvedGraph.from_constraint_graph gienv
856 let global_inference_merge_and_solve
857 ~verbosity ?(error_format = Errors.Raw) ?max_errors ctx gienvs =
858 print_global_inference_envs ctx ~verbosity gienvs;
859 let gienv = merge_global_inference_envs_opt ctx gienvs in
860 print_merged_global_inference_env ~verbosity gienv error_format max_errors;
861 let gienv = Option.map gienv solve_global_inference_env in
862 print_solved_global_inference_env ~verbosity gienv error_format max_errors;
863 gienv
865 let check_file ctx ~verbosity errors files_info error_format max_errors =
866 let (errors, tasts, genvs) =
867 Relative_path.Map.fold
868 files_info
870 begin
871 fun fn fileinfo (errors, tasts, genvs) ->
872 let (new_tasts, new_genvs, new_errors) =
873 Typing_check_utils.type_file_with_global_tvenvs ctx fn fileinfo
875 ( errors @ Errors.get_sorted_error_list new_errors,
876 new_tasts @ tasts,
877 Lazy.force new_genvs @ genvs )
879 ~init:(errors, [], [])
881 let gienvs =
882 Typing_global_inference.StateSubConstraintGraphs.build ctx tasts genvs
884 let _gienv =
885 global_inference_merge_and_solve
887 ~verbosity:(verbosity + 1)
888 gienvs
889 ~error_format
890 ?max_errors
892 errors
894 let create_nasts ctx files_info =
895 let build_nast fn _ =
896 let ast = Ast_provider.get_ast ~full:true ctx fn in
897 Naming.program ctx ast
899 Relative_path.Map.mapi ~f:build_nast files_info
901 let parse_and_name ctx files_contents =
902 let parsed_files =
903 Relative_path.Map.mapi files_contents ~f:(fun fn contents ->
904 Errors.run_in_context fn Errors.Parsing (fun () ->
905 let popt = Provider_context.get_tcopt ctx in
906 let parsed_file =
907 Full_fidelity_ast.defensive_program popt fn contents
909 let ast =
910 let { Parser_return.ast; _ } = parsed_file in
911 if ParserOptions.deregister_php_stdlib popt then
912 Nast.deregister_ignored_attributes ast
913 else
916 Ast_provider.provide_ast_hint fn ast Ast_provider.Full;
917 { parsed_file with Parser_return.ast }))
919 let files_info =
920 Relative_path.Map.mapi
922 begin
923 fun _fn parsed_file ->
924 let { Parser_return.file_mode; comments; ast; _ } = parsed_file in
925 (* If the feature is turned on, deregister functions with attribute
926 __PHPStdLib. This does it for all functions, not just hhi files *)
927 let (funs, classes, record_defs, typedefs, consts) =
928 Nast.get_defs ast
931 FileInfo.file_mode;
932 funs;
933 classes;
934 record_defs;
935 typedefs;
936 consts;
937 comments = Some comments;
938 hash = None;
941 parsed_files
943 Relative_path.Map.iter files_info (fun fn fileinfo ->
944 Errors.run_in_context fn Errors.Naming (fun () ->
945 let { FileInfo.funs; classes; record_defs; typedefs; consts; _ } =
946 fileinfo
948 Naming_global.make_env
950 ~funs
951 ~classes
952 ~record_defs
953 ~typedefs
954 ~consts));
955 (parsed_files, files_info)
957 let parse_name_and_decl ctx files_contents =
958 Errors.do_ (fun () ->
959 let (parsed_files, files_info) = parse_and_name ctx files_contents in
960 Relative_path.Map.iter parsed_files (fun fn _ ->
961 Errors.run_in_context fn Errors.Decl (fun () ->
962 Decl.make_env ~sh:SharedMem.Uses ctx fn));
964 files_info)
966 let parse_name_and_shallow_decl ctx filename file_contents :
967 Shallow_decl_defs.shallow_class SMap.t =
968 Errors.ignore_ (fun () ->
969 let files_contents = Relative_path.Map.singleton filename file_contents in
970 let (parsed_files, _) = parse_and_name ctx files_contents in
971 let parsed_file = Relative_path.Map.values parsed_files |> List.hd_exn in
972 parsed_file.Parser_return.ast
973 |> List.filter_map ~f:(function
974 | Aast.Class c -> Some (Shallow_decl.class_ ctx c)
975 | _ -> None)
976 |> List.fold ~init:SMap.empty ~f:(fun acc c ->
977 SMap.add (snd c.Shallow_decl_defs.sc_name) c acc))
979 let test_shallow_class_diff popt filename =
980 let filename_after = Relative_path.to_absolute filename ^ ".after" in
981 let contents1 = Sys_utils.cat (Relative_path.to_absolute filename) in
982 let contents2 = Sys_utils.cat filename_after in
983 let decls1 = parse_name_and_shallow_decl popt filename contents1 in
984 let decls2 = parse_name_and_shallow_decl popt filename contents2 in
985 let decls =
986 SMap.merge (fun _ a b -> Some (a, b)) decls1 decls2 |> SMap.bindings
988 let diffs =
989 List.map decls (fun (cid, old_and_new) ->
990 ( Utils.strip_ns cid,
991 match old_and_new with
992 | (Some c1, Some c2) -> Shallow_class_diff.diff_class c1 c2
993 | _ -> ClassDiff.Major_change ))
995 List.iter diffs (fun (cid, diff) ->
996 Format.printf "%s: %a@." cid ClassDiff.pp diff)
998 let add_newline contents =
999 (* this is used for incremental mode to change all the positions, so we
1000 basically want a prepend; there's a few cases we need to handle:
1001 - empty file
1002 - header line: apppend after header
1003 - shebang and header: append after header
1004 - shebang only, no header (e.g. .hack file): append after shebang
1005 - no header or shebang (e.g. .hack file): prepend
1007 let after_shebang =
1008 if string_starts_with contents "#!" then
1009 String.index_exn contents '\n' + 1
1010 else
1013 let after_header =
1015 String.length contents > after_shebang + 2
1016 && String.sub contents after_shebang 2 = "<?"
1017 then
1018 String.index_from_exn contents after_shebang '\n' + 1
1019 else
1020 after_shebang
1022 String.sub contents 0 after_header
1023 ^ "\n"
1024 ^ String.sub contents after_header (String.length contents - after_header)
1026 let get_decls defs =
1027 ( SSet.fold
1028 (fun x acc -> Decl_heap.Typedefs.find_unsafe x :: acc)
1029 defs.FileInfo.n_types
1031 SSet.fold
1032 (fun x acc -> Decl_heap.Funs.find_unsafe x :: acc)
1033 defs.FileInfo.n_funs
1035 SSet.fold
1036 (fun x acc -> Decl_heap.Classes.find_unsafe x :: acc)
1037 defs.FileInfo.n_classes
1038 [] )
1040 let fail_comparison s =
1041 raise
1042 (Failure
1043 ( Printf.sprintf "Comparing %s failed!\n" s
1044 ^ "It's likely that you added new positions to decl types "
1045 ^ "without updating Decl_pos_utils.NormalizeSig\n" ))
1047 let compare_typedefs t1 t2 =
1048 let t1 = Decl_pos_utils.NormalizeSig.typedef t1 in
1049 let t2 = Decl_pos_utils.NormalizeSig.typedef t2 in
1050 if t1 <> t2 then fail_comparison "typedefs"
1052 let compare_funs f1 f2 =
1053 let f1 = Decl_pos_utils.NormalizeSig.fun_elt f1 in
1054 let f2 = Decl_pos_utils.NormalizeSig.fun_elt f2 in
1055 if f1 <> f2 then fail_comparison "funs"
1057 let compare_classes c1 c2 =
1058 if Decl_compare.class_big_diff c1 c2 then fail_comparison "class_big_diff";
1060 let c1 = Decl_pos_utils.NormalizeSig.class_type c1 in
1061 let c2 = Decl_pos_utils.NormalizeSig.class_type c2 in
1062 let (_, is_unchanged) =
1063 Decl_compare.ClassDiff.compare c1.Decl_defs.dc_name c1 c2
1065 if not is_unchanged then fail_comparison "ClassDiff";
1067 let (_, is_unchanged) = Decl_compare.ClassEltDiff.compare c1 c2 in
1068 if is_unchanged = `Changed then fail_comparison "ClassEltDiff"
1070 let test_decl_compare ctx filenames builtins files_contents files_info =
1071 (* skip some edge cases that we don't handle now... ugly! *)
1072 if Relative_path.suffix filenames = "capitalization3.php" then
1074 else if Relative_path.suffix filenames = "capitalization4.php" then
1076 else
1077 (* do not analyze builtins over and over *)
1078 let files_info =
1079 Relative_path.Map.fold
1080 builtins
1082 begin
1083 fun k _ acc ->
1084 Relative_path.Map.remove acc k
1086 ~init:files_info
1088 let files =
1089 Relative_path.Map.fold
1090 files_info
1091 ~f:(fun k _ acc -> Relative_path.Set.add acc k)
1092 ~init:Relative_path.Set.empty
1094 let defs =
1095 Relative_path.Map.fold
1096 files_info
1098 begin
1099 fun _ names1 names2 ->
1100 FileInfo.(merge_names (simplify names1) names2)
1102 ~init:FileInfo.empty_names
1104 let (typedefs1, funs1, classes1) = get_decls defs in
1105 (* For the purpose of this test, we can ignore other heaps *)
1106 Ast_provider.remove_batch files;
1108 let get_classes path =
1109 match Relative_path.Map.find_opt files_info path with
1110 | None -> SSet.empty
1111 | Some info -> SSet.of_list @@ List.map info.FileInfo.classes snd
1113 (* We need to oldify, not remove, for ClassEltDiff to work *)
1114 Decl_redecl_service.oldify_type_decl
1116 None
1117 get_classes
1118 ~bucket_size:1
1119 ~previously_oldified_defs:FileInfo.empty_names
1120 ~defs
1121 ~collect_garbage:false;
1123 let files_contents = Relative_path.Map.map files_contents ~f:add_newline in
1124 let (_, _) = parse_name_and_decl ctx files_contents in
1125 let (typedefs2, funs2, classes2) = get_decls defs in
1126 List.iter2_exn typedefs1 typedefs2 compare_typedefs;
1127 List.iter2_exn funs1 funs2 compare_funs;
1128 List.iter2_exn classes1 classes2 compare_classes;
1131 (* Returns a list of Tast defs, along with associated type environments. *)
1132 let compute_tasts ctx files_info interesting_files :
1133 Errors.t
1134 * ( Tast.program Relative_path.Map.t
1135 * Typing_inference_env.t_global_with_pos list ) =
1136 let _f _k nast x =
1137 match (nast, x) with
1138 | (Some nast, Some _) -> Some nast
1139 | _ -> None
1141 Errors.do_ (fun () ->
1142 let nasts = create_nasts ctx files_info in
1143 (* Interesting files are usually the non hhi ones. *)
1144 let filter_non_interesting nasts =
1145 Relative_path.Map.merge nasts interesting_files (fun _k nast x ->
1146 match (nast, x) with
1147 | (Some nast, Some _) -> Some nast
1148 | _ -> None)
1150 let nasts = filter_non_interesting nasts in
1151 let tasts_envs =
1152 Relative_path.Map.map
1153 nasts
1154 ~f:(Typing_toplevel.nast_to_tast_gienv ~do_tast_checks:true ctx)
1156 let tasts = Relative_path.Map.map tasts_envs ~f:fst in
1157 let genvs =
1158 List.concat
1159 @@ Relative_path.Map.values
1160 @@ Relative_path.Map.map tasts_envs ~f:snd
1162 (tasts, genvs))
1164 let merge_global_inference_env_in_tast gienv tast =
1165 let env_merger =
1166 object
1167 inherit Tast_visitor.endo
1169 method! on_'en _ env =
1171 env with
1172 Tast.inference_env =
1173 Typing_inference_env.simple_merge
1174 env.Tast.inference_env
1175 gienv.inference_env;
1179 env_merger#go tast
1182 * Compute TASTs for some files, then expand all type variables.
1184 let compute_tasts_expand_types ctx ~verbosity files_info interesting_files =
1185 let (errors, (tasts, gienvs)) =
1186 compute_tasts ctx files_info interesting_files
1188 let subconstraints =
1189 Typing_global_inference.StateSubConstraintGraphs.build
1191 (List.concat (Relative_path.Map.values tasts))
1192 gienvs
1194 let (tasts, gi_solved) =
1195 match global_inference_merge_and_solve ctx ~verbosity subconstraints with
1196 | None -> (tasts, None)
1197 | Some ((gienv, _, _) as gi_solved) ->
1198 let tasts =
1199 Relative_path.Map.map
1200 tasts
1201 (merge_global_inference_env_in_tast gienv ctx)
1203 (tasts, Some gi_solved)
1205 let tasts = Relative_path.Map.map tasts (Tast_expand.expand_program ctx) in
1206 (errors, tasts, gi_solved)
1208 let print_tasts tasts ctx =
1209 let print_tast = Typing_ast_print.print_tast ctx in
1210 Relative_path.Map.iter tasts (fun _k (tast : Tast.program) -> print_tast tast)
1212 let typecheck_tasts tasts tcopt (filename : Relative_path.t) =
1213 let env = Typing_env.empty tcopt filename ~droot:None in
1214 let tasts = Relative_path.Map.values tasts in
1215 let typecheck_tast tast =
1216 Errors.get_sorted_error_list (Tast_typecheck.check env tast)
1218 List.concat_map tasts ~f:typecheck_tast
1220 let pp_debug_deps fmt entries =
1221 Format.fprintf fmt "@[<v>";
1222 ignore
1223 @@ List.fold_left entries ~init:false ~f:(fun sep (obj, roots) ->
1224 if sep then Format.fprintf fmt "@;";
1225 Format.fprintf fmt "%s -> " obj;
1226 Format.fprintf fmt "@[<hv>";
1227 ignore
1228 @@ List.fold_left roots ~init:false ~f:(fun sep root ->
1229 if sep then Format.fprintf fmt ",@ ";
1230 Format.pp_print_string fmt root;
1231 true);
1232 Format.fprintf fmt "@]";
1233 true);
1234 Format.fprintf fmt "@]"
1236 let show_debug_deps = Format.asprintf "%a" pp_debug_deps
1238 let sort_debug_deps deps =
1239 Hashtbl.fold deps ~init:[] ~f:(fun ~key:obj ~data:set acc ->
1240 (obj, set) :: acc)
1241 |> List.sort ~compare:(fun (a, _) (b, _) -> String.compare a b)
1242 |> List.map ~f:(fun (obj, roots) ->
1243 let roots =
1244 HashSet.fold roots ~init:[] ~f:List.cons
1245 |> List.sort ~compare:String.compare
1247 (obj, roots))
1249 (* Note: this prints dependency graph edges in the same direction as the mapping
1250 which is actually stored in the shared memory table. The line "X -> Y" can be
1251 read, "X is used by Y", or "X is a dependency of Y", or "when X changes, Y
1252 must be rechecked". *)
1253 let dump_debug_deps dbg_deps =
1254 dbg_deps |> sort_debug_deps |> show_debug_deps |> Printf.printf "%s\n"
1256 let dump_debug_glean_deps
1257 (deps :
1258 ( Typing_deps.Dep.dependency Typing_deps.Dep.variant
1259 * Typing_deps.Dep.dependent Typing_deps.Dep.variant )
1260 HashSet.t) =
1261 let json_opt = Glean_dependency_graph.convert_deps_to_json ~deps in
1262 match json_opt with
1263 | Some json_obj ->
1264 Printf.printf "%s\n" (Hh_json.json_to_string ~pretty:true json_obj)
1265 | None -> Printf.printf "No dependencies\n"
1267 let scan_files_for_symbol_index
1268 (filename : Relative_path.t)
1269 (sienv : SearchUtils.si_env)
1270 (ctx : Provider_context.t) : SearchUtils.si_env =
1271 let files_contents = Multifile.file_to_files filename in
1272 let (_, individual_file_info) = parse_name_and_decl ctx files_contents in
1273 let fileinfo_list = Relative_path.Map.values individual_file_info in
1274 let transformed_list =
1275 List.map fileinfo_list ~f:(fun fileinfo ->
1276 (filename, SearchUtils.Full fileinfo, SearchUtils.TypeChecker))
1278 SymbolIndex.update_files ~ctx ~sienv ~paths:transformed_list
1280 let handle_mode
1281 mode
1282 filenames
1284 builtins
1285 files_contents
1286 files_info
1287 parse_errors
1288 max_errors
1289 error_format
1290 batch_mode
1291 out_extension
1292 dbg_deps
1293 dbg_glean_deps
1294 ~verbosity
1295 (sienv : SearchUtils.si_env) =
1296 let expect_single_file () : Relative_path.t =
1297 match filenames with
1298 | [x] -> x
1299 | _ -> die "Only single file expected"
1301 let iter_over_files f : unit = List.iter filenames f in
1302 match mode with
1303 | Ifc (mode, lattice) ->
1304 let print_errors errors = List.iter ~f:(print_error error_format) errors in
1305 if not (List.is_empty parse_errors) then
1306 print_errors parse_errors
1307 else
1308 let errors =
1309 check_file ~verbosity ctx [] files_info error_format max_errors
1311 if not (List.is_empty errors) then
1312 print_errors errors
1313 else
1314 let opts =
1315 match Ifc_options.parse ~mode ~lattice with
1316 | Ok opts -> opts
1317 | Error e -> die ("Could not parse IFC options: " ^ e)
1319 let ifc_errors = Ifc.do_ opts files_info ctx in
1320 if not @@ List.is_empty ifc_errors then begin
1321 print_errors ifc_errors;
1322 exit 2
1324 | Ai ai_options ->
1325 if not (List.is_empty parse_errors) then
1326 List.iter ~f:(print_error error_format) parse_errors
1327 else
1328 let to_check =
1329 Relative_path.Map.filter files_info ~f:(fun _p i ->
1330 let open FileInfo in
1331 match i.file_mode with
1332 | None
1333 | Some Mstrict ->
1334 true
1335 | _ -> false)
1337 let errors =
1338 check_file ~verbosity ctx [] to_check error_format max_errors
1340 if not (List.is_empty errors) then
1341 List.iter ~f:(print_error error_format) errors
1342 else
1343 Ai.do_ files_info ai_options ctx
1344 | Autocomplete
1345 | Autocomplete_manually_invoked ->
1346 let path = expect_single_file () in
1347 let contents = cat (Relative_path.to_absolute path) in
1348 (* Search backwards: there should only be one /real/ case. If there's multiple, *)
1349 (* guess that the others are preceding explanation comments *)
1350 let offset =
1351 Str.search_backward
1352 (Str.regexp AutocompleteTypes.autocomplete_token)
1353 contents
1354 (String.length contents)
1356 let pos = File_content.offset_to_position contents offset in
1357 let is_manually_invoked = mode = Autocomplete_manually_invoked in
1358 let (ctx, entry) =
1359 Provider_context.add_or_overwrite_entry_contents ~ctx ~path ~contents
1361 let autocomplete_context =
1362 ServerAutoComplete.get_autocomplete_context
1363 ~file_content:contents
1364 ~pos
1365 ~is_manually_invoked
1367 let sienv = scan_files_for_symbol_index path sienv ctx in
1368 let result =
1369 ServerAutoComplete.go_at_auto332_ctx
1370 ~ctx
1371 ~entry
1372 ~sienv
1373 ~autocomplete_context
1375 List.iter
1377 begin
1378 fun r ->
1379 AutocompleteTypes.(Printf.printf "%s %s\n" r.res_name r.res_ty)
1381 result.Utils.With_complete_flag.value
1382 | Ffp_autocomplete ->
1383 iter_over_files (fun path ->
1385 let sienv = scan_files_for_symbol_index path sienv ctx in
1386 let (ctx, entry) = Provider_context.add_entry_if_missing ~ctx ~path in
1387 (* TODO: Use a magic word/symbol to identify autocomplete location instead *)
1388 let args_regex = Str.regexp "AUTOCOMPLETE [1-9][0-9]* [1-9][0-9]*" in
1389 let position =
1391 let file_text = Provider_context.read_file_contents_exn entry in
1392 let _ = Str.search_forward args_regex file_text 0 in
1393 let raw_flags = Str.matched_string file_text in
1394 match split ' ' raw_flags with
1395 | [_; row; column] ->
1396 { line = int_of_string row; column = int_of_string column }
1397 | _ -> failwith "Invalid test file: no flags found"
1398 with Caml.Not_found ->
1399 failwith "Invalid test file: no flags found"
1401 let result =
1402 FfpAutocompleteService.auto_complete
1404 entry
1405 position
1406 ~filter_by_token:true
1407 ~sienv
1409 match result with
1410 | [] -> Printf.printf "No result found\n"
1411 | res ->
1412 List.iter res ~f:(fun r ->
1413 AutocompleteTypes.(Printf.printf "%s\n" r.res_name))
1414 with
1415 | Failure msg
1416 | Invalid_argument msg ->
1417 Printf.printf "%s\n" msg;
1418 exit 1)
1419 | Color ->
1420 Relative_path.Map.iter files_info (fun fn fileinfo ->
1421 if Relative_path.Map.mem builtins fn then
1423 else
1424 let (tast, _) = Typing_check_utils.type_file ctx fn fileinfo in
1425 let result = Coverage_level.get_levels ctx tast fn in
1426 match result with
1427 | Ok result -> print_colored fn result
1428 | Error () ->
1429 failwith
1430 ("HH_FIXMEs not found for path " ^ Relative_path.to_absolute fn))
1431 | Coverage ->
1432 Relative_path.Map.iter files_info (fun fn fileinfo ->
1433 if Relative_path.Map.mem builtins fn then
1435 else
1436 let (tast, _) = Typing_check_utils.type_file ctx fn fileinfo in
1437 let type_acc = ServerCoverageMetric.accumulate_types ctx tast fn in
1438 print_coverage type_acc)
1439 | Cst_search ->
1440 let path = expect_single_file () in
1441 let (ctx, entry) = Provider_context.add_entry_if_missing ~ctx ~path in
1442 let result =
1443 let open Result.Monad_infix in
1444 Sys_utils.read_stdin_to_string ()
1445 |> Hh_json.json_of_string
1446 |> CstSearchService.compile_pattern ctx
1447 >>| CstSearchService.search ctx entry
1448 >>| CstSearchService.result_to_json ~sort_results:true
1449 >>| Hh_json.json_to_string ~pretty:true
1451 begin
1452 match result with
1453 | Ok result -> Printf.printf "%s\n" result
1454 | Error message ->
1455 Printf.printf "%s\n" message;
1456 exit 1
1458 | Dump_symbol_info ->
1459 iter_over_files (fun filename ->
1460 match Relative_path.Map.find_opt files_info filename with
1461 | Some fileinfo ->
1462 let raw_result =
1463 SymbolInfoService.helper ctx [] [(filename, fileinfo)]
1465 let result = SymbolInfoService.format_result raw_result in
1466 let result_json = ClientSymbolInfo.to_json result in
1467 print_endline (Hh_json.json_to_multiline result_json)
1468 | None -> ())
1469 | Lint ->
1470 let lint_errors =
1471 Relative_path.Map.fold
1472 files_contents
1473 ~init:[]
1474 ~f:(fun fn content lint_errors ->
1475 lint_errors
1476 @ fst (Lint.do_ (fun () -> Linting_service.lint ctx fn content)))
1478 if lint_errors <> [] then (
1479 let lint_errors =
1480 List.sort
1481 ~compare:
1482 begin
1483 fun x y ->
1484 Pos.compare (Lint.get_pos x) (Lint.get_pos y)
1486 lint_errors
1488 let lint_errors = List.map ~f:Lint.to_absolute lint_errors in
1489 ServerLint.output_text stdout lint_errors error_format;
1490 exit 2
1491 ) else
1492 Printf.printf "No lint errors\n"
1493 | Dump_deps ->
1494 Relative_path.Map.iter files_info (fun fn fileinfo ->
1495 ignore @@ Typing_check_utils.check_defs ctx fn fileinfo);
1496 if Hashtbl.length dbg_deps > 0 then dump_debug_deps dbg_deps
1497 | Dump_glean_deps ->
1498 Relative_path.Map.iter files_info (fun fn fileinfo ->
1499 ignore @@ Typing_check_utils.check_defs ctx fn fileinfo);
1500 dump_debug_glean_deps dbg_glean_deps
1501 | Dump_inheritance ->
1502 let open ServerCommandTypes.Method_jumps in
1503 let naming_table = Naming_table.create files_info in
1504 Naming_table.iter naming_table Typing_deps.Files.update_file;
1505 Naming_table.iter naming_table (fun fn fileinfo ->
1506 if Relative_path.Map.mem builtins fn then
1508 else (
1509 List.iter fileinfo.FileInfo.classes (fun (_p, class_) ->
1510 Printf.printf
1511 "Ancestors of %s and their overridden methods:\n"
1512 class_;
1513 let ancestors =
1514 MethodJumps.get_inheritance
1516 class_
1517 ~filter:No_filter
1518 ~find_children:false
1519 naming_table
1520 None
1522 ClientMethodJumps.print_readable ancestors ~find_children:false;
1523 Printf.printf "\n");
1524 Printf.printf "\n";
1525 List.iter fileinfo.FileInfo.classes (fun (_p, class_) ->
1526 Printf.printf
1527 "Children of %s and the methods they override:\n"
1528 class_;
1529 let children =
1530 MethodJumps.get_inheritance
1532 class_
1533 ~filter:No_filter
1534 ~find_children:true
1535 naming_table
1536 None
1538 ClientMethodJumps.print_readable children ~find_children:true;
1539 Printf.printf "\n")
1541 | Identify_symbol (line, column) ->
1542 let path = expect_single_file () in
1543 let (ctx, entry) = Provider_context.add_entry_if_missing ~ctx ~path in
1544 (* TODO(ljw): surely this doesn't need quarantine? *)
1545 let result =
1546 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
1547 ServerIdentifyFunction.go_quarantined_absolute
1548 ~ctx
1549 ~entry
1550 ~line
1551 ~column)
1553 begin
1554 match result with
1555 | [] -> print_endline "None"
1556 | result -> ClientGetDefinition.print_readable ~short_pos:true result
1558 | Find_local (line, char) ->
1559 let filename = expect_single_file () in
1560 let (ctx, entry) =
1561 Provider_context.add_entry_if_missing ~ctx ~path:filename
1563 let result = ServerFindLocals.go ~ctx ~entry ~line ~char in
1564 let print pos = Printf.printf "%s\n" (Pos.string_no_file pos) in
1565 List.iter result print
1566 | Outline ->
1567 iter_over_files (fun filename ->
1568 let file = cat (Relative_path.to_absolute filename) in
1569 let results =
1570 FileOutline.outline (Provider_context.get_popt ctx) file
1572 FileOutline.print ~short_pos:true results)
1573 | Dump_nast ->
1574 iter_over_files (fun filename ->
1575 let nasts = create_nasts ctx files_info in
1576 let nast = Relative_path.Map.find nasts filename in
1577 Printf.printf "%s\n" (Nast.show_program nast))
1578 | Dump_tast ->
1579 let (errors, tasts, _gi_solved) =
1580 compute_tasts_expand_types ctx ~verbosity files_info files_contents
1582 print_errors_if_present (parse_errors @ Errors.get_sorted_error_list errors);
1583 print_tasts tasts ctx
1584 | Check_tast ->
1585 iter_over_files (fun filename ->
1586 let files_contents =
1587 Relative_path.Map.filter files_contents ~f:(fun k _v -> k = filename)
1589 let (errors, tasts, _gi_solved) =
1590 compute_tasts_expand_types ctx ~verbosity files_info files_contents
1592 print_tasts tasts ctx;
1593 if not @@ Errors.is_empty errors then (
1594 print_errors error_format errors max_errors;
1595 Printf.printf "Did not typecheck the TAST as there are typing errors.";
1596 exit 2
1597 ) else
1598 let tast_check_errors = typecheck_tasts tasts ctx filename in
1599 print_error_list error_format tast_check_errors max_errors;
1600 if tast_check_errors <> [] then exit 2)
1601 | Dump_stripped_tast ->
1602 iter_over_files (fun filename ->
1603 let files_contents =
1604 Relative_path.Map.filter files_contents ~f:(fun k _v -> k = filename)
1606 let (_, (tasts, _gienvs)) =
1607 compute_tasts ctx files_info files_contents
1609 let tast = Relative_path.Map.find tasts filename in
1610 let nast = Tast.to_nast tast in
1611 Printf.printf "%s\n" (Nast.show_program nast))
1612 | Rewrite ->
1613 let (errors, _tasts, gi_solved) =
1614 compute_tasts_expand_types ctx ~verbosity files_info files_contents
1616 print_errors_if_present (parse_errors @ Errors.get_sorted_error_list errors);
1617 (match gi_solved with
1618 | None ->
1619 prerr_endline
1620 ( "error: no patches generated as global"
1621 ^ " inference is turend off (use --global-inference)" );
1622 exit 1
1623 | Some gi_solved ->
1624 let patches =
1625 ServerGlobalInference.Mode_rewrite.get_patches ~files_contents gi_solved
1627 if List.length patches <= 0 then
1628 print_endline "No patches"
1629 else
1630 (* simple key-map: convert Relative_path.Map.t into an SMap.t
1631 * without changing the values *)
1632 let file_contents =
1633 Relative_path.Map.fold
1634 files_contents
1635 ~f:(fun fn -> SMap.add (Relative_path.suffix fn))
1636 ~init:SMap.empty
1638 let patched =
1639 ClientRefactor.apply_patches_to_file_contents file_contents patches
1641 let print_filename = not @@ Int.equal (SMap.cardinal patched) 1 in
1642 SMap.iter
1643 (fun fn new_contents ->
1644 if print_filename then Printf.printf "//// %s\n" fn;
1645 Out_channel.output_string stdout new_contents)
1646 patched)
1647 | Find_refs (line, column) ->
1648 let path = expect_single_file () in
1649 let naming_table = Naming_table.create files_info in
1650 Naming_table.iter naming_table Typing_deps.Files.update_file;
1651 let genv = ServerEnvBuild.default_genv in
1652 let init_id = Random_id.short_string () in
1653 let env =
1655 (ServerEnvBuild.make_env ~init_id genv.ServerEnv.config) with
1656 ServerEnv.naming_table;
1657 ServerEnv.tcopt = Provider_context.get_tcopt ctx;
1660 let include_defs = true in
1661 let (ctx, entry) =
1662 Provider_context.add_entry_if_missing
1663 ~ctx:(Provider_utils.ctx_from_server_env env)
1664 ~path
1666 let open Option.Monad_infix in
1667 let open ServerCommandTypes.Done_or_retry in
1668 let results =
1669 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
1670 ServerFindRefs.(
1671 go_from_file_ctx ~ctx ~entry ~line ~column >>= fun (name, action) ->
1672 go ctx action include_defs genv env
1673 |> map_env ~f:(to_ide name)
1674 |> snd
1675 |> function
1676 | Done r -> r
1677 | Retry ->
1678 failwith
1679 @@ "should only happen with prechecked files "
1680 ^ "which are not a thing in hh_single_type_check"))
1682 ClientFindRefs.print_ide_readable results
1683 | Go_to_impl (line, column) ->
1684 let filename = expect_single_file () in
1685 let naming_table = Naming_table.create files_info in
1686 Naming_table.iter naming_table Typing_deps.Files.update_file;
1687 let genv = ServerEnvBuild.default_genv in
1688 let init_id = Random_id.short_string () in
1689 let env =
1691 (ServerEnvBuild.make_env ~init_id genv.ServerEnv.config) with
1692 ServerEnv.naming_table;
1693 ServerEnv.tcopt = Provider_context.get_tcopt ctx;
1696 let filename = Relative_path.to_absolute filename in
1697 let contents = cat filename in
1698 let (ctx, entry) =
1699 Provider_context.add_or_overwrite_entry_contents
1700 ~ctx:(Provider_utils.ctx_from_server_env env)
1701 ~path:(Relative_path.create_detect_prefix filename)
1702 ~contents
1704 Option.Monad_infix.(
1705 ServerCommandTypes.Done_or_retry.(
1706 let results =
1707 ServerFindRefs.go_from_file_ctx ~ctx ~entry ~line ~column
1708 >>= fun (name, action) ->
1709 ServerGoToImpl.go ~action ~genv ~env
1710 |> map_env ~f:(ServerFindRefs.to_ide name)
1711 |> snd
1712 |> function
1713 | Done r -> r
1714 | Retry ->
1715 failwith
1716 @@ "should only happen with prechecked files "
1717 ^ "which are not a thing in hh_single_type_check"
1719 ClientFindRefs.print_ide_readable results))
1720 | Highlight_refs (line, column) ->
1721 let path = expect_single_file () in
1722 let (ctx, entry) = Provider_context.add_entry_if_missing ~ctx ~path in
1723 let results =
1724 ServerHighlightRefs.go_quarantined ~ctx ~entry ~line ~column
1726 ClientHighlightRefs.go results ~output_json:false
1727 | Errors when batch_mode ->
1728 (* For each file in our batch, run typechecking serially.
1729 Reset the heaps every time in between. *)
1730 iter_over_files (fun filename ->
1731 let oc =
1732 Out_channel.create (Relative_path.to_absolute filename ^ out_extension)
1734 (* This means builtins had errors, so lets just print those if we see them *)
1735 if parse_errors <> [] then
1736 (* This closes the out channel *)
1737 write_error_list error_format parse_errors oc max_errors
1738 else (
1739 Typing_log.out_channel := oc;
1740 Provider_utils.respect_but_quarantine_unsaved_changes
1741 ~ctx
1742 ~f:(fun () ->
1743 let files_contents = Multifile.file_to_files filename in
1744 let (parse_errors, individual_file_info) =
1745 parse_name_and_decl ctx files_contents
1747 let errors =
1748 check_file
1750 ~verbosity
1751 (Errors.get_sorted_error_list parse_errors)
1752 individual_file_info
1753 error_format
1754 max_errors
1756 write_error_list error_format errors oc max_errors)
1758 | Decl_compare when batch_mode ->
1759 (* For each file in our batch, run typechecking serially.
1760 Reset the heaps every time in between. *)
1761 iter_over_files (fun filename ->
1762 let oc =
1763 Out_channel.create (Relative_path.to_absolute filename ^ ".decl_out")
1765 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
1766 let files_contents =
1767 Relative_path.Map.filter files_contents ~f:(fun k _v ->
1768 k = filename)
1770 let (_, individual_file_info) =
1771 parse_name_and_decl ctx files_contents
1774 test_decl_compare
1776 filename
1777 builtins
1778 files_contents
1779 individual_file_info;
1780 Out_channel.output_string oc ""
1781 with e ->
1782 let msg = Exn.to_string e in
1783 Out_channel.output_string oc msg);
1784 Out_channel.close oc)
1785 | Errors ->
1786 (* Don't typecheck builtins *)
1787 let errors =
1788 check_file ctx ~verbosity parse_errors files_info error_format max_errors
1790 print_error_list error_format errors max_errors;
1791 if errors <> [] then exit 2
1792 | Decl_compare ->
1793 let filename = expect_single_file () in
1794 test_decl_compare ctx filename builtins files_contents files_info
1795 | Shallow_class_diff ->
1796 print_errors_if_present parse_errors;
1797 let filename = expect_single_file () in
1798 test_shallow_class_diff ctx filename
1799 | Linearization ->
1800 if parse_errors <> [] then (
1801 print_error error_format (List.hd_exn parse_errors);
1802 exit 2
1804 let files_info =
1805 Relative_path.Map.fold
1806 builtins
1808 begin
1809 fun k _ acc ->
1810 Relative_path.Map.remove acc k
1812 ~init:files_info
1814 Relative_path.Map.iter files_info ~f:(fun _file info ->
1815 let { FileInfo.classes; _ } = info in
1816 List.iter classes ~f:(fun (_, classname) ->
1817 Printf.printf "Linearization for class %s:\n" classname;
1818 let key = (classname, Decl_defs.Member_resolution) in
1819 let linearization = Decl_linearize.get_linearization ctx key in
1820 let linearization =
1821 Sequence.map linearization (fun mro ->
1822 let name = mro.Decl_defs.mro_name in
1823 let targs =
1824 List.map
1825 mro.Decl_defs.mro_type_args
1826 (Typing_print.full_decl ctx)
1828 let targs =
1829 if targs = [] then
1831 else
1832 "<" ^ String.concat ~sep:"," targs ^ ">"
1834 Decl_defs.(
1835 let modifiers =
1837 ( if Option.is_some mro.mro_required_at then
1838 Some "requirement"
1839 else if
1840 is_set mro_via_req_extends mro.mro_flags
1841 || is_set mro_via_req_impl mro.mro_flags
1842 then
1843 Some "synthesized"
1844 else
1845 None );
1846 ( if is_set mro_xhp_attrs_only mro.mro_flags then
1847 Some "xhp_attrs_only"
1848 else
1849 None );
1850 ( if is_set mro_consts_only mro.mro_flags then
1851 Some "consts_only"
1852 else
1853 None );
1854 ( if is_set mro_copy_private_members mro.mro_flags then
1855 Some "copy_private_members"
1856 else
1857 None );
1858 ( if
1859 is_set
1860 mro_passthrough_abstract_typeconst
1861 mro.mro_flags
1862 then
1863 Some "PAT"
1864 else
1865 None );
1866 Option.map mro.mro_trait_reuse ~f:(fun c ->
1867 "trait reuse via " ^ c);
1869 |> List.filter_map ~f:(fun x -> x)
1870 |> String.concat ~sep:", "
1872 Printf.sprintf
1873 "%s%s%s"
1874 name
1875 targs
1876 ( if String.equal modifiers "" then
1878 else
1879 Printf.sprintf "(%s)" modifiers )))
1880 |> Sequence.to_list
1882 Printf.printf "[%s]\n" (String.concat ~sep:", " linearization)))
1884 (*****************************************************************************)
1885 (* Main entry point *)
1886 (*****************************************************************************)
1888 let decl_and_run_mode
1890 files;
1891 mode;
1892 error_format;
1893 no_builtins;
1894 tcopt;
1895 max_errors;
1896 batch_mode;
1897 out_extension;
1898 verbosity;
1900 (popt : TypecheckerOptions.t)
1901 (hhi_root : Path.t)
1902 (sienv : SearchUtils.si_env) : unit =
1903 Ident.track_names := true;
1904 let builtins =
1905 if no_builtins then
1906 Relative_path.Map.empty
1907 else
1908 (* Note that the regular `.hhi` files have already been written to disk
1909 with `Hhi.get_root ()` *)
1910 let magic_builtins =
1911 match mode with
1912 | Ai _ -> Array.append magic_builtins Ai.magic_builtins
1913 | Ifc _ -> Array.append magic_builtins Ifc.magic_builtins
1914 | _ -> magic_builtins
1916 Array.iter magic_builtins ~f:(fun (file_name, file_contents) ->
1917 let file_path = Path.concat hhi_root file_name in
1918 let file = Path.to_string file_path in
1919 Sys_utils.try_touch
1920 (Sys_utils.Touch_existing { follow_symlinks = true })
1921 file;
1922 Sys_utils.write_file ~file file_contents);
1924 (* Take the builtins (file, contents) array and create relative paths *)
1925 Array.fold
1926 (Array.append magic_builtins hhi_builtins)
1927 ~init:Relative_path.Map.empty
1928 ~f:(fun acc (f, src) ->
1929 let f = Path.concat hhi_root f |> Path.to_string in
1930 Relative_path.Map.add
1932 ~key:(Relative_path.create Relative_path.Hhi f)
1933 ~data:src)
1935 let files = List.map ~f:(Relative_path.create Relative_path.Dummy) files in
1936 let files_contents =
1937 List.fold
1938 files
1939 ~f:(fun acc filename ->
1940 let files_contents = Multifile.file_to_files filename in
1941 Relative_path.Map.union acc files_contents)
1942 ~init:Relative_path.Map.empty
1944 (* Merge in builtins *)
1945 let files_contents_with_builtins =
1946 Relative_path.Map.fold
1947 builtins
1949 begin
1950 fun k src acc ->
1951 Relative_path.Map.add acc ~key:k ~data:src
1953 ~init:files_contents
1955 (* Don't declare all the filenames in batch_errors mode *)
1956 let to_decl =
1957 if batch_mode then
1958 builtins
1959 else
1960 files_contents_with_builtins
1962 let dbg_deps = Hashtbl.Poly.create () in
1963 ( if mode = Dump_deps then
1964 (* In addition to actually recording the dependencies in shared memory,
1965 we build a non-hashed respresentation of the dependency graph
1966 for printing. *)
1967 let get_debug_trace root obj =
1968 let root = Typing_deps.Dep.variant_to_string root in
1969 let obj = Typing_deps.Dep.variant_to_string obj in
1970 match Hashtbl.find dbg_deps obj with
1971 | Some set -> HashSet.add set root
1972 | None ->
1973 let set = HashSet.create () in
1974 HashSet.add set root;
1975 Hashtbl.set dbg_deps obj set
1977 Typing_deps.add_dependency_callback "get_debug_trace" get_debug_trace );
1978 let dbg_glean_deps = HashSet.create () in
1979 ( if mode = Dump_glean_deps then
1980 (* In addition to actually recording the dependencies in shared memory,
1981 we build a non-hashed respresentation of the dependency graph
1982 for printing. In the callback we receive this as dep_right uses dep_left. *)
1983 let get_debug_trace dep_right dep_left =
1984 HashSet.add dbg_glean_deps (dep_left, dep_right)
1986 Typing_deps.add_dependency_callback "get_debug_trace" get_debug_trace );
1987 let ctx = Provider_context.empty_for_test ~popt ~tcopt in
1988 let (errors, files_info) = parse_name_and_decl ctx to_decl in
1989 handle_mode
1990 mode
1991 files
1993 builtins
1994 files_contents
1995 files_info
1996 (Errors.get_sorted_error_list errors)
1997 max_errors
1998 error_format
1999 batch_mode
2000 out_extension
2001 dbg_deps
2002 dbg_glean_deps
2003 sienv
2004 ~verbosity
2006 let main_hack ({ tcopt; _ } as opts) (sienv : SearchUtils.si_env) : unit =
2007 (* TODO: We should have a per file config *)
2008 Sys_utils.signal Sys.sigusr1 (Sys.Signal_handle Typing.debug_print_last_pos);
2009 EventLogger.init_fake ();
2011 let (_handle : SharedMem.handle) =
2012 SharedMem.init ~num_workers:0 SharedMem.default_config
2014 Tempfile.with_tempdir (fun hhi_root ->
2015 Hhi.set_hhi_root_for_unit_test hhi_root;
2016 Relative_path.set_path_prefix Relative_path.Root (Path.make "/");
2017 Relative_path.set_path_prefix Relative_path.Hhi hhi_root;
2018 Relative_path.set_path_prefix Relative_path.Tmp (Path.make "tmp");
2019 decl_and_run_mode opts tcopt hhi_root sienv;
2020 TypingLogger.flush_buffers ())
2022 (* command line driver *)
2023 let () =
2024 if !Sys.interactive then
2026 else
2027 (* On windows, setting 'binary mode' avoids to output CRLF on
2028 stdout. The 'text mode' would not hurt the user in general, but
2029 it breaks the testsuite where the output is compared to the
2030 expected one (i.e. in given file without CRLF). *)
2031 Out_channel.set_binary_mode stdout true;
2032 let (options, sienv) = parse_options () in
2033 Unix.handle_unix_error main_hack options sienv