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.
15 module Inf
= Typing_inference_env
17 module StringAnnotation
= struct
20 let pp fmt str
= Format.pp_print_string fmt str
23 module PS
= Full_fidelity_positioned_syntax
24 module PositionedTree
= Full_fidelity_syntax_tree.WithSyntax
(PS
)
26 (*****************************************************************************)
27 (* Types, constants *)
28 (*****************************************************************************)
31 | Ifc
of string * string
34 | Autocomplete_manually_invoked
44 | Identify_symbol
of int * int
45 | Find_local
of int * int
52 | Find_refs
of int * int
53 | Highlight_refs
of int * int
57 | Go_to_impl
of int * int
63 error_format
: Errors.format
;
65 max_errors
: int option;
66 tcopt
: GlobalOptions.t
;
68 out_extension
: string;
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 *)
79 ( "hh_single_type_check_magic.hhi",
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 () {}"
90 ^
"namespace HH\\Lib\\Tuple{\n"
92 ^
"function from_async();\n"
96 (*****************************************************************************)
98 (*****************************************************************************)
102 Out_channel.output_string
oc str
;
103 Out_channel.close
oc;
106 let print_error format ?
(oc = stderr
) l
=
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
;
124 Errors.format_summary
127 (List.length dropped_errors
)
130 | Some summary
-> Out_channel.output_string
oc summary
133 Out_channel.output_string
oc "No errors\n";
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
;
145 Errors.format_summary
148 (List.length dropped_errors
)
151 | Some summary
-> Out_channel.output_string stderr summary
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
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
178 if !mode <> Errors
then
179 raise
(Arg.Bad
"only a single mode should be specified")
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
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'" );
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",
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");
285 Arg.Unit
(set_mode Cst_search
),
286 " Search the concrete syntax tree of the given file using the pattern"
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" );
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");
304 " Don't use builtins (e.g. ConstSet)" );
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",
318 Arg.Int
(fun x
-> line := x
);
320 (fun column
-> set_mode (Identify_symbol
(!line, column
)) ());
322 "<pos> Show info about symbol at given line and column" );
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" );
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");
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." );
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." );
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."
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" );
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",
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" );
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." );
394 Arg.Unit
(set_bool dynamic_view),
395 " Turns on dynamic view, replacing Tany with dynamic" );
397 Arg.Unit
(set_bool unsafe_rx),
398 " Disables reactivity related errors" );
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." );
406 Arg.Int
(fun secs
-> timeout := Some secs
),
407 " Timeout in seconds for checking a function or a class." );
411 Arg.String
(fun x
-> log_key := x
);
413 (fun level
-> log_levels := SMap.add
!log_key level
!log_levels);
415 " Set the log level for a key" );
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" );
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",
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");
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" );
514 Arg.String
(fun str
-> glean_service := str
),
515 " glean service name" );
516 ( "--glean-hostname",
517 Arg.String
(fun str
-> glean_hostname := str
),
519 ("--glean-port", Arg.Int
(fun x
-> glean_port := x
), " glean port number");
520 ( "--glean-reponame",
521 Arg.String
(fun str
-> glean_reponame := str
),
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."
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",
566 allowed_fixme_codes_strict :=
567 Str.split
(Str.regexp
", *") s
568 |> List.map ~f
:int_of_string
570 "List of fixmes that are allowed in strict mode." );
571 ( "--allowed-fixme-codes-partial",
574 allowed_fixme_codes_partial :=
575 Str.split
(Str.regexp
", *") s
576 |> List.map ~f
:int_of_string
578 "List of fixmes that are allowed in partial mode." );
579 ( "--codes-not-raised-partial",
582 codes_not_raised_partial :=
583 Str.split
(Str.regexp
", *") s
584 |> List.map ~f
:int_of_string
586 "List of error codes that are not raised in partial mode." );
587 ( "--allowed-decl-fixme-codes",
590 allowed_decl_fixme_codes :=
591 Str.split
(Str.regexp
", *") s
592 |> List.map ~f
:int_of_string
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;
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;
695 GlobalOptions.tco_experimental_features
=
699 if x
= GlobalOptions.tco_experimental_forbid_nullable_cast
then
700 !forbid_nullable_cast
704 tcopt.GlobalOptions.tco_experimental_features
;
707 (* Configure symbol index settings *)
708 let namespace_map = GlobalOptions.po_auto_namespace_map
tcopt in
710 SymbolIndex.initialize
714 ~provider_name
:"LocalIndex"
716 ~ignore_hh_version
:false
717 ~savedstate_file_opt
:!symbolindex_file
723 SearchUtils.sie_resolve_signatures
= true;
724 SearchUtils.sie_resolve_positions
= true;
725 SearchUtils.sie_resolve_local_decl
= true;
731 no_builtins = !no_builtins;
732 max_errors = !max_errors;
733 error_format = !error_format;
735 batch_mode = !batch_mode;
736 out_extension = !out_extension;
737 verbosity = !verbosity;
741 (* Make readable test output *)
742 let replace_color input
=
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)
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
=
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
) =
773 when Filename.check_suffix
774 (Relative_path.suffix
(Pos.filename pos
))
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
)
800 let print_global_inference_env
801 env ~step_name state_errors
error_format max_errors =
804 print_endline
(String.map s
(const '
='
));
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
);
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
821 (gienv
: Typing_global_inference.StateConstraintGraph.t
option)
824 if verbosity >= 1 then
827 | Some
(_type_map
, gienv
, state_errors
) ->
828 print_global_inference_env
835 let print_solved_global_inference_env
837 (gienv
: Typing_global_inference.StateSolvedGraph.t
option)
840 if verbosity >= 1 then
843 | Some
(gienv
, state_errors
, _type_map
) ->
844 print_global_inference_env
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;
865 let check_file ctx ~
verbosity errors files_info
error_format max_errors =
866 let (errors
, tasts
, genvs
) =
867 Relative_path.Map.fold
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
,
877 Lazy.force new_genvs
@ genvs
)
879 ~init
:(errors
, [], [])
882 Typing_global_inference.StateSubConstraintGraphs.build ctx tasts genvs
885 global_inference_merge_and_solve
887 ~
verbosity:(verbosity + 1)
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
=
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
907 Full_fidelity_ast.defensive_program
popt fn contents
910 let { Parser_return.ast; _
} = parsed_file in
911 if ParserOptions.deregister_php_stdlib
popt then
912 Nast.deregister_ignored_attributes
ast
916 Ast_provider.provide_ast_hint fn
ast Ast_provider.Full
;
917 { parsed_file with Parser_return.ast }))
920 Relative_path.Map.mapi
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
) =
937 comments
= Some comments
;
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
; _
} =
948 Naming_global.make_env
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
));
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
)
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
986 SMap.merge
(fun _ a b
-> Some
(a
, b
)) decls1 decls2 |> SMap.bindings
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:
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
1008 if string_starts_with contents
"#!" then
1009 String.index_exn contents '
\n'
+ 1
1015 String.length contents
> after_shebang + 2
1016 && String.sub contents
after_shebang 2 = "<?"
1018 String.index_from_exn contents
after_shebang '
\n'
+ 1
1022 String.sub contents
0 after_header
1024 ^
String.sub contents
after_header (String.length contents
- after_header)
1026 let get_decls defs
=
1028 (fun x acc
-> Decl_heap.Typedefs.find_unsafe x
:: acc
)
1029 defs
.FileInfo.n_types
1032 (fun x acc
-> Decl_heap.Funs.find_unsafe x
:: acc
)
1033 defs
.FileInfo.n_funs
1036 (fun x acc
-> Decl_heap.Classes.find_unsafe x
:: acc
)
1037 defs
.FileInfo.n_classes
1040 let fail_comparison s
=
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
1077 (* do not analyze builtins over and over *)
1079 Relative_path.Map.fold
1084 Relative_path.Map.remove acc k
1089 Relative_path.Map.fold
1091 ~f
:(fun k _ acc
-> Relative_path.Set.add acc k
)
1092 ~init
:Relative_path.Set.empty
1095 Relative_path.Map.fold
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
1119 ~previously_oldified_defs
:FileInfo.empty_names
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
:
1134 * ( Tast.program
Relative_path.Map.t
1135 * Typing_inference_env.t_global_with_pos list
) =
1137 match (nast
, x
) with
1138 | (Some nast
, Some _
) -> Some nast
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
1150 let nasts = filter_non_interesting nasts in
1152 Relative_path.Map.map
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
1159 @@ Relative_path.Map.values
1160 @@ Relative_path.Map.map
tasts_envs ~f
:snd
1164 let merge_global_inference_env_in_tast gienv tast
=
1167 inherit Tast_visitor.endo
1169 method! on_'en _
env =
1172 Tast.inference_env
=
1173 Typing_inference_env.simple_merge
1174 env.Tast.inference_env
1175 gienv.inference_env
;
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))
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
) ->
1199 Relative_path.Map.map
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>";
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>";
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
;
1232 Format.fprintf fmt
"@]";
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
->
1241 |> List.sort ~compare
:(fun (a
, _
) (b
, _
) -> String.compare a b
)
1242 |> List.map ~f
:(fun (obj
, roots
) ->
1244 HashSet.fold
roots ~init
:[] ~f
:List.cons
1245 |> List.sort ~compare
:String.compare
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
1258 ( Typing_deps.Dep.dependency
Typing_deps.Dep.variant
1259 * Typing_deps.Dep.dependent
Typing_deps.Dep.variant
)
1261 let json_opt = Glean_dependency_graph.convert_deps_to_json ~deps
in
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
1295 (sienv : SearchUtils.si_env
) =
1296 let expect_single_file () : Relative_path.t
=
1297 match filenames
with
1299 | _
-> die "Only single file expected"
1301 let iter_over_files f
: unit = List.iter filenames f
in
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
1309 check_file ~
verbosity ctx
[] files_info error_format max_errors
1311 if not
(List.is_empty
errors) then
1315 match Ifc_options.parse ~
mode ~lattice
with
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;
1325 if not
(List.is_empty parse_errors
) then
1326 List.iter ~f
:(print_error error_format) parse_errors
1329 Relative_path.Map.filter
files_info ~f
:(fun _p i
->
1330 let open FileInfo
in
1331 match i
.file_mode
with
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
1343 Ai.do_
files_info ai_options ctx
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 *)
1352 (Str.regexp
AutocompleteTypes.autocomplete_token
)
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
1359 Provider_context.add_or_overwrite_entry_contents ~ctx ~
path ~
contents
1361 let autocomplete_context =
1362 ServerAutoComplete.get_autocomplete_context
1363 ~file_content
:contents
1365 ~
is_manually_invoked
1367 let sienv = scan_files_for_symbol_index path sienv ctx
in
1369 ServerAutoComplete.go_at_auto332_ctx
1373 ~
autocomplete_context
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
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"
1402 FfpAutocompleteService.auto_complete
1406 ~filter_by_token
:true
1410 | [] -> Printf.printf
"No result found\n"
1412 List.iter res ~f
:(fun r
->
1413 AutocompleteTypes.(Printf.printf
"%s\n" r
.res_name
))
1416 | Invalid_argument msg
->
1417 Printf.printf
"%s\n" msg
;
1420 Relative_path.Map.iter
files_info (fun fn fileinfo
->
1421 if Relative_path.Map.mem builtins fn
then
1424 let (tast
, _) = Typing_check_utils.type_file ctx fn fileinfo
in
1425 let result = Coverage_level.get_levels ctx tast fn
in
1427 | Ok
result -> print_colored fn
result
1430 ("HH_FIXMEs not found for path " ^
Relative_path.to_absolute fn
))
1432 Relative_path.Map.iter
files_info (fun fn fileinfo
->
1433 if Relative_path.Map.mem builtins fn
then
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)
1440 let path = expect_single_file () in
1441 let (ctx
, entry
) = Provider_context.add_entry_if_missing ~ctx ~
path in
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
1453 | Ok
result -> Printf.printf
"%s\n" result
1455 Printf.printf
"%s\n" message
;
1458 | Dump_symbol_info
->
1459 iter_over_files (fun filename
->
1460 match Relative_path.Map.find_opt
files_info filename
with
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)
1471 Relative_path.Map.fold
1474 ~f
:(fun fn
content lint_errors ->
1476 @ fst
(Lint.do_
(fun () -> Linting_service.lint ctx fn
content)))
1478 if lint_errors <> [] then (
1484 Pos.compare
(Lint.get_pos x
) (Lint.get_pos y
)
1488 let lint_errors = List.map ~f
:Lint.to_absolute
lint_errors in
1489 ServerLint.output_text stdout
lint_errors error_format;
1492 Printf.printf
"No lint errors\n"
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
1509 List.iter fileinfo
.FileInfo.classes
(fun (_p
, class_
) ->
1511 "Ancestors of %s and their overridden methods:\n"
1514 MethodJumps.get_inheritance
1518 ~find_children
:false
1522 ClientMethodJumps.print_readable
ancestors ~find_children
:false;
1523 Printf.printf
"\n");
1525 List.iter fileinfo
.FileInfo.classes
(fun (_p
, class_
) ->
1527 "Children of %s and the methods they override:\n"
1530 MethodJumps.get_inheritance
1538 ClientMethodJumps.print_readable
children ~find_children
:true;
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? *)
1546 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f
:(fun () ->
1547 ServerIdentifyFunction.go_quarantined_absolute
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
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
1567 iter_over_files (fun filename ->
1568 let file = cat
(Relative_path.to_absolute
filename) in
1570 FileOutline.outline
(Provider_context.get_popt ctx
) file
1572 FileOutline.print ~short_pos
:true results)
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))
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
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.";
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))
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
1620 ( "error: no patches generated as global"
1621 ^
" inference is turend off (use --global-inference)" );
1625 ServerGlobalInference.Mode_rewrite.get_patches ~
files_contents gi_solved
1627 if List.length
patches <= 0 then
1628 print_endline
"No patches"
1630 (* simple key-map: convert Relative_path.Map.t into an SMap.t
1631 * without changing the values *)
1633 Relative_path.Map.fold
1635 ~f
:(fun fn
-> SMap.add
(Relative_path.suffix fn
))
1639 ClientRefactor.apply_patches_to_file_contents
file_contents patches
1641 let print_filename = not
@@ Int.equal
(SMap.cardinal
patched) 1 in
1643 (fun fn new_contents
->
1644 if print_filename then Printf.printf
"//// %s\n" fn
;
1645 Out_channel.output_string stdout new_contents
)
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
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
1662 Provider_context.add_entry_if_missing
1663 ~ctx
:(Provider_utils.ctx_from_server_env
env)
1666 let open Option.Monad_infix
in
1667 let open ServerCommandTypes.Done_or_retry
in
1669 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f
:(fun () ->
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
)
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
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
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)
1704 Option.Monad_infix.(
1705 ServerCommandTypes.Done_or_retry.(
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
)
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
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 ->
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
1739 Typing_log.out_channel
:= oc;
1740 Provider_utils.respect_but_quarantine_unsaved_changes
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
1751 (Errors.get_sorted_error_list parse_errors
)
1752 individual_file_info
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 ->
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
->
1770 let (_, individual_file_info
) =
1771 parse_name_and_decl ctx
files_contents
1779 individual_file_info
;
1780 Out_channel.output_string
oc ""
1782 let msg = Exn.to_string e
in
1783 Out_channel.output_string
oc msg);
1784 Out_channel.close
oc)
1786 (* Don't typecheck builtins *)
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
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
1800 if parse_errors
<> [] then (
1801 print_error error_format (List.hd_exn parse_errors
);
1805 Relative_path.Map.fold
1810 Relative_path.Map.remove acc k
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
1821 Sequence.map
linearization (fun mro
->
1822 let name = mro
.Decl_defs.mro_name
in
1825 mro
.Decl_defs.mro_type_args
1826 (Typing_print.full_decl ctx
)
1832 "<" ^
String.concat ~sep
:"," targs ^
">"
1837 ( if Option.is_some mro
.mro_required_at
then
1840 is_set mro_via_req_extends mro
.mro_flags
1841 || is_set mro_via_req_impl mro
.mro_flags
1846 ( if is_set mro_xhp_attrs_only mro
.mro_flags
then
1847 Some
"xhp_attrs_only"
1850 ( if is_set mro_consts_only mro
.mro_flags
then
1854 ( if is_set mro_copy_private_members mro
.mro_flags
then
1855 Some
"copy_private_members"
1860 mro_passthrough_abstract_typeconst
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
:", "
1876 ( if String.equal
modifiers "" then
1879 Printf.sprintf
"(%s)" modifiers )))
1882 Printf.printf
"[%s]\n" (String.concat ~sep
:", " linearization)))
1884 (*****************************************************************************)
1885 (* Main entry point *)
1886 (*****************************************************************************)
1888 let decl_and_run_mode
1900 (popt : TypecheckerOptions.t
)
1902 (sienv : SearchUtils.si_env
) : unit =
1903 Ident.track_names
:= true;
1906 Relative_path.Map.empty
1908 (* Note that the regular `.hhi` files have already been written to disk
1909 with `Hhi.get_root ()` *)
1910 let magic_builtins =
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
1920 (Sys_utils.Touch_existing
{ follow_symlinks
= true })
1922 Sys_utils.write_file ~
file file_contents);
1924 (* Take the builtins (file, contents) array and create relative paths *)
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)
1935 let files = List.map ~
f:(Relative_path.create
Relative_path.Dummy
) files in
1936 let files_contents =
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
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 *)
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
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
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
1996 (Errors.get_sorted_error_list
errors)
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 *)
2024 if !Sys.interactive
then
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