Change from FAST to path list
[hiphop-php.git] / hphp / hack / src / server / serverTypeCheck.ml
blob25909eab928d3f01f6281995b4df6f9d3f0fa09c
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 ServerCheckUtils
12 open SearchServiceRunner
13 open ServerEnv
14 open Reordered_argument_collections
15 module SLC = ServerLocalConfig
17 type check_kind =
18 (* Lazy check is a check limited to the files open in IDE. It:
19 * - produces push diagnostics for those files
20 * - updates their parsing / naming / decl definitions on heap
21 * - updates their parsing level indexes, like SymbolIndex or
22 * ServerEnv.naming_table
23 * - invalidates their declaration dependencies, by removing them from the
24 * heap and depending on lazy declaration to redeclare them on
25 * as-needed basis later
26 * - stores the information about what it skipped doing to be finished later
27 * by Full_check
29 * It does not do the "full" expensive fanout:
30 * - does not re-declare dependencies ("phase 2 decl")
31 * - does not fan out to all typing dependencies
32 * - because of that, it does not update structures depending on global state,
33 * like global error list, dependency table or the lists of files that
34 * failed parsing / declaration / checking
36 * Any operation that need the global state to be up to date and cannot get
37 * the data that they need through lazy decl, need to be preceded by
38 * Full_check. *)
39 | Lazy_check
40 (* Full check brings the global state of the server to consistency by
41 * executing all the re-checks that lazy checks delayed. It processes the
42 * disk updates and typechecks the full fanout of accumulated changes. *)
43 | Full_check
45 type check_results = {
46 reparse_count: int;
47 total_rechecked_count: int;
50 let shallow_decl_enabled (ctx : Provider_context.t) =
51 TypecheckerOptions.shallow_class_decl ctx.Provider_context.tcopt
53 (*****************************************************************************)
54 (* Debugging *)
55 (*****************************************************************************)
57 (** Something is removing diagnostic subscriptions, but I don't know what... *)
58 let log_if_diag_subscribe_changed
59 (title : string)
60 ~(before : Diagnostic_subscription.t option)
61 ~(after : Diagnostic_subscription.t option) : unit =
62 match (before, after) with
63 | (None, None)
64 | (Some _, Some _) ->
66 | _ ->
67 let stack =
68 Caml.Printexc.get_callstack 100 |> Caml.Printexc.raw_backtrace_to_string
70 let disposition =
71 if before = None then
72 "added"
73 else
74 "removed"
76 Hh_logger.log "Diag_subscribe: %s - %s!\n%s" title disposition stack
78 let print_defs prefix defs =
79 List.iter defs (fun (_, fname) -> Printf.printf " %s %s\n" prefix fname)
81 let print_fast_pos fast_pos =
82 SMap.iter fast_pos (fun x (funs, classes) ->
83 Printf.printf "File: %s\n" x;
84 print_defs "Fun" funs;
85 print_defs "Class" classes);
86 Printf.printf "\n";
87 Out_channel.flush stdout;
90 let print_fast fast =
91 SMap.iter fast (fun x (funs, classes) ->
92 Printf.printf "File: %s\n" x;
93 SSet.iter funs (Printf.printf " Fun %s\n");
94 SSet.iter classes (Printf.printf " Class %s\n"));
95 Printf.printf "\n";
96 Out_channel.flush stdout;
99 let debug_print_path_set genv name set =
100 ServerDebug.log genv (fun () ->
101 Hh_json.(
102 let files =
103 Relative_path.Set.fold set ~init:[] ~f:(fun k acc ->
104 JSON_String (Relative_path.suffix k) :: acc)
106 JSON_Object
108 ("type", JSON_String "incremental_files");
109 ("name", JSON_String name);
110 ("files", JSON_Array files);
113 let debug_print_fast_keys genv name fast =
114 ServerDebug.log genv (fun () ->
115 Hh_json.(
116 let files =
117 Relative_path.Map.fold fast ~init:[] ~f:(fun k _v acc ->
118 JSON_String (Relative_path.suffix k) :: acc)
120 let decls =
121 Relative_path.Map.fold fast ~init:[] ~f:(fun _k v acc ->
122 let {
123 FileInfo.n_funs;
124 n_classes;
125 n_record_defs;
126 n_types;
127 n_consts;
131 let prepend_json_strings decls acc =
132 SSet.fold decls ~init:acc ~f:(fun n acc -> JSON_String n :: acc)
134 let acc = prepend_json_strings n_funs acc in
135 let acc = prepend_json_strings n_classes acc in
136 let acc = prepend_json_strings n_record_defs acc in
137 let acc = prepend_json_strings n_types acc in
138 let acc = prepend_json_strings n_consts acc in
139 acc)
141 JSON_Object
143 ("type", JSON_String "incremental_files");
144 ("name", JSON_String name);
145 ("files", JSON_Array files);
146 ("decls", JSON_Array decls);
149 (*****************************************************************************)
150 (* Given a set of Ast.id list produce a SSet.t (got rid of the positions) *)
151 (*****************************************************************************)
153 let set_of_idl l =
154 List.fold_left l ~f:(fun acc (_, x) -> SSet.add acc x) ~init:SSet.empty
156 (*****************************************************************************)
157 (* We want add all the declarations that were present in a file *before* the
158 * current modification. The scenario:
159 * File foo.php was defining the class A.
160 * The user gets rid of class A (in foo.php)
161 * In general, the type-checker determines what must be re-declared or
162 * re-typechecked, by comparing the old and the new type-definitions.
163 * That's why we are adding the 'old' definitions to the file.
164 * In this case, the redecl phase (typing/typing_redecl_service.ml) is going
165 * to compare the 'old' definition of A with the new one. It will realize that
166 * the new one is missing, and go ahead and retype everything that depends
167 * on A.
168 * Without a call to add_old_decls, the class A wouldn't appear anywhere,
169 * and we wouldn't realize that we have to re-check the types that depend
170 * on A.
172 (*****************************************************************************)
174 let add_old_decls old_naming_table fast =
175 Relative_path.Map.fold
176 fast
178 begin
179 fun filename info_names acc ->
180 match Naming_table.get_file_info old_naming_table filename with
181 | None -> acc
182 | Some old_info ->
183 let old_info_names = FileInfo.simplify old_info in
184 let info_names = FileInfo.merge_names old_info_names info_names in
185 Relative_path.Map.add acc ~key:filename ~data:info_names
187 ~init:fast
189 (*****************************************************************************)
190 (* Removes the names that were defined in the files *)
191 (*****************************************************************************)
193 let remove_decls env fast_parsed =
194 Relative_path.Map.iter fast_parsed (fun fn _ ->
195 match Naming_table.get_file_info env.naming_table fn with
196 | None -> ()
197 | Some
199 FileInfo.funs = funl;
200 classes = classel;
201 record_defs = record_defsl;
202 typedefs = typel;
203 consts = constl;
204 file_mode = _;
205 comments = _;
206 hash = _;
207 } ->
208 let ctx = Provider_utils.ctx_from_server_env env in
209 let funs = set_of_idl funl in
210 let classes = set_of_idl classel in
211 let record_defs = set_of_idl record_defsl in
212 let typedefs = set_of_idl typel in
213 let consts = set_of_idl constl in
214 Naming_global.remove_decls
215 ~ctx
216 ~funs
217 ~classes
218 ~record_defs
219 ~typedefs
220 ~consts)
222 (* If the only things that would change about file analysis are positions,
223 * we're not going to recheck it, and positions in its error list might
224 * become stale. Look if any of those positions refer to files that have
225 * actually changed and add them to files to recheck. *)
226 let get_files_with_stale_errors
227 ~(* Set of files that were reparsed (so their ASTs and positions
228 * in them could have changed. *)
229 reparsed
230 ~(* A subset of files which errors we want to update, or None if we want
231 * to update entire error list. *)
232 filter
233 ~(* Consider errors only coming from those phases *)
234 phases
235 ~(* Current global error list *)
236 errors =
237 let fold =
238 match filter with
239 | None ->
240 fun phase init f ->
241 (* Looking at global files *)
242 Errors.fold_errors errors ~phase ~init ~f:(fun source error acc ->
243 f source error acc)
244 | Some sources ->
245 fun phase init f ->
246 (* Looking only at subset of error sources *)
247 Relative_path.Set.fold sources ~init ~f:(fun source acc ->
248 Errors.fold_errors_in
249 errors
250 ~source
251 ~phase
252 ~init:acc
253 ~f:(fun error acc -> f source error acc))
255 List.fold phases ~init:Relative_path.Set.empty ~f:(fun acc phase ->
256 fold phase acc (fun source error acc ->
258 List.exists (Errors.to_list error) ~f:(fun e ->
259 Relative_path.Set.mem reparsed (fst e |> Pos.filename))
260 then
261 Relative_path.Set.add acc source
262 else
263 acc))
265 (*****************************************************************************)
266 (* Parses the set of modified files *)
267 (*****************************************************************************)
269 (* Even when we remove an IDE file that failed after parsing stage, it might
270 * appear again in later stages - we need to filter it every time we extend
271 * the set of files to process *)
272 let remove_failed_parsing_map fast ~stop_at_errors env failed_parsing =
273 if stop_at_errors then
274 Relative_path.Map.filter fast ~f:(fun k _ ->
276 @@ Relative_path.(
277 Set.mem failed_parsing k && Set.mem env.editor_open_files k))
278 else
279 fast
281 let remove_failed_parsing_set fast ~stop_at_errors env failed_parsing =
282 if stop_at_errors then
283 Relative_path.Set.filter fast ~f:(fun k ->
285 @@ Relative_path.(
286 Set.mem failed_parsing k && Set.mem env.editor_open_files k))
287 else
288 fast
290 let parsing genv env to_check ~stop_at_errors =
291 let (ide_files, disk_files) =
292 Relative_path.Set.partition
293 (Relative_path.Set.mem env.editor_open_files)
294 to_check
296 File_provider.remove_batch disk_files;
297 Ast_provider.remove_batch disk_files;
298 Fixme_provider.remove_batch disk_files;
300 if stop_at_errors then (
301 File_provider.local_changes_push_stack ();
302 Ast_provider.local_changes_push_stack ();
303 Fixme_provider.local_changes_push_stack ()
306 (* Do not remove ide files from file heap *)
307 Ast_provider.remove_batch ide_files;
308 Fixme_provider.remove_batch ide_files;
310 let env =
312 env with
313 local_symbol_table =
314 SymbolIndex.remove_files ~sienv:env.local_symbol_table ~paths:to_check;
317 SharedMem.collect `gentle;
318 let get_next =
319 MultiWorker.next genv.workers (Relative_path.Set.elements disk_files)
321 let (fast, errors, failed_parsing) =
322 Parsing_service.go genv.workers ide_files ~get_next env.popt ~trace:true
324 SearchServiceRunner.update_fileinfo_map
325 (Naming_table.create fast)
326 SearchUtils.TypeChecker;
328 (* During integration tests, we want to pretend that search is run
329 synchronously *)
330 let ctx = Provider_utils.ctx_from_server_env env in
331 let env =
333 env with
334 local_symbol_table =
335 (let sie = env.local_symbol_table in
337 SearchServiceRunner.should_run_completely
338 genv
339 sie.SearchUtils.sie_provider
340 then
341 SearchServiceRunner.run_completely ctx sie
342 else
343 sie);
347 if stop_at_errors then (
348 (* Revert changes and ignore results for IDE files that failed parsing *)
349 let ide_failed_parsing = Relative_path.Set.inter failed_parsing ide_files in
350 let fast =
351 remove_failed_parsing_map fast stop_at_errors env ide_failed_parsing
353 let ide_success_parsing =
354 Relative_path.Set.diff ide_files ide_failed_parsing
356 File_provider.local_changes_revert_batch failed_parsing;
357 Ast_provider.local_changes_revert_batch ide_failed_parsing;
358 Fixme_provider.local_changes_revert_batch ide_failed_parsing;
360 File_provider.local_changes_commit_batch ide_success_parsing;
361 Ast_provider.local_changes_commit_batch ide_success_parsing;
362 Fixme_provider.local_changes_commit_batch ide_success_parsing;
363 Ast_provider.local_changes_commit_batch disk_files;
364 Fixme_provider.local_changes_commit_batch disk_files;
366 File_provider.local_changes_pop_stack ();
367 Ast_provider.local_changes_pop_stack ();
368 Fixme_provider.local_changes_pop_stack ();
370 (env, fast, errors, failed_parsing)
371 ) else
372 (env, fast, errors, failed_parsing)
374 (*****************************************************************************)
375 (* At any given point in time, we want to know what each file defines.
376 * The datastructure that maintains this information is called file_info.
377 * This code updates the file information.
379 (*****************************************************************************)
381 let update_naming_table env fast_parsed =
382 Relative_path.Map.iter fast_parsed Typing_deps.update_file;
383 let naming_table = Naming_table.update_many env.naming_table fast_parsed in
384 naming_table
386 (*****************************************************************************)
387 (* Defining the global naming environment.
388 * Defines an environment with the names of all the globals (classes/funs).
390 (*****************************************************************************)
392 let declare_names env fast_parsed =
393 (* We need to do naming phase for files that failed naming before, even
394 * if they were not re-parsed in this iteration, so we are extending
395 * fast_parsed with them. *)
396 let fast_parsed =
397 Relative_path.Set.fold env.failed_naming ~init:fast_parsed ~f:(fun k acc ->
398 match Relative_path.Map.find_opt acc k with
399 | Some _ -> acc (* the file was re-parsed already *)
400 | None ->
401 (* The file was not re-parsed, so it's correct to look up its contents
402 * in (old) env. *)
403 (match Naming_table.get_file_info env.naming_table k with
404 | None -> acc
405 (* this should not happen - failed_naming should be
406 a subset of keys in naming_table *)
407 | Some v -> Relative_path.Map.add acc k v))
409 remove_decls env fast_parsed;
410 let ctx = Provider_utils.ctx_from_server_env env in
411 let (errorl, failed_naming) =
412 Relative_path.Map.fold
413 fast_parsed
415 begin
416 fun k v (errorl, failed) ->
417 let (errorl', failed') = Naming_global.ndecl_file ctx k v in
418 let errorl = Errors.merge errorl' errorl in
419 let failed = Relative_path.Set.union failed' failed in
420 (errorl, failed)
422 ~init:(Errors.empty, Relative_path.Set.empty)
424 let fast = Naming_table.to_fast (Naming_table.create fast_parsed) in
425 (errorl, failed_naming, fast)
427 let diff_set_and_map_keys set map =
428 Relative_path.Map.fold map ~init:set ~f:(fun k _ acc ->
429 Relative_path.Set.remove acc k)
431 let union_set_and_map_keys set map =
432 Relative_path.Map.fold map ~init:set ~f:(fun k _ acc ->
433 Relative_path.Set.add acc k)
435 let get_interrupt_config genv env =
436 MultiThreadedCall.{ handlers = env.interrupt_handlers genv; env }
438 (*****************************************************************************)
439 (* Where the action is! *)
440 (*****************************************************************************)
442 module type CheckKindType = sig
443 (* Parsing treats files open in IDE and files coming from disk differently:
445 * - for IDE files, we need to look up their contents in the map in env,
446 * instead of reading from disk (duh)
447 * - we parse IDE files in master process (to avoid passing env to the
448 * workers)
449 * - to make the IDE more responsive, we try to shortcut the typechecking at
450 * the parsing level if there were parsing errors
452 val get_files_to_parse : ServerEnv.env -> Relative_path.Set.t * bool
454 (* files to parse, should we stop if there are parsing errors *)
456 val get_defs_to_redecl :
457 reparsed:Relative_path.Set.t -> env:ServerEnv.env -> Relative_path.Set.t
459 (* Returns a tuple: files to redecl now, files to redecl later *)
460 val get_defs_to_redecl_phase2 :
461 ServerEnv.genv ->
462 decl_defs:Naming_table.fast ->
463 naming_table:Naming_table.t ->
464 to_redecl_phase2:Relative_path.Set.t ->
465 env:ServerEnv.env ->
466 Naming_table.fast * Naming_table.fast
468 val get_to_recheck2_approximation :
469 to_redecl_phase2_deps:Typing_deps.DepSet.t ->
470 env:ServerEnv.env ->
471 Relative_path.Set.t
473 (* Which files to typecheck, based on results of declaration phase *)
474 val get_defs_to_recheck :
475 reparsed:Relative_path.Set.t ->
476 phase_2_decl_defs:Naming_table.fast ->
477 to_recheck:Relative_path.Set.t ->
478 env:ServerEnv.env ->
479 Relative_path.Set.t * Relative_path.Set.t
481 (* Update the global state based on resuts of parsing, naming and decl *)
482 val get_env_after_decl :
483 old_env:ServerEnv.env ->
484 naming_table:Naming_table.t ->
485 failed_naming:Relative_path.Set.t ->
486 ServerEnv.env
488 (* Update the global state based on resuts of typing *)
489 val get_env_after_typing :
490 old_env:ServerEnv.env ->
491 errorl:Errors.t ->
492 needs_phase2_redecl:Relative_path.Set.t ->
493 needs_recheck:Relative_path.Set.t ->
494 diag_subscribe:Diagnostic_subscription.t option ->
495 ServerEnv.env
497 val is_full : bool
500 module FullCheckKind : CheckKindType = struct
501 let get_files_to_parse env =
502 let files_to_parse =
503 Relative_path.Set.(env.ide_needs_parsing |> union env.disk_needs_parsing)
505 (files_to_parse, false)
507 let get_defs_to_redecl ~reparsed ~env =
508 (* Besides the files that actually changed, we want to also redeclare
509 * those that have decl errors referring to files that were
510 * reparsed, since positions in those errors can be now stale *)
511 get_files_with_stale_errors
512 ~reparsed
513 ~filter:None
514 ~phases:[Errors.Decl]
515 ~errors:env.errorl
517 let get_defs_to_redecl_phase2
518 genv ~decl_defs ~naming_table ~to_redecl_phase2 ~env =
519 let fast = extend_fast genv decl_defs naming_table to_redecl_phase2 in
520 (* Add decl fanout that was delayed by previous lazy checks to phase 2 *)
521 let fast = extend_fast genv fast naming_table env.needs_phase2_redecl in
522 (fast, Relative_path.Map.empty)
524 let get_to_recheck2_approximation ~to_redecl_phase2_deps:_ ~env:_ =
525 (* Full check is computing to_recheck2 set accurately, so there is no need
526 * to approximate anything *)
527 Relative_path.Set.empty
529 let get_defs_to_recheck ~reparsed ~phase_2_decl_defs ~to_recheck ~env =
530 (* Besides the files that actually changed, we want to also recheck
531 * those that have typing errors referring to files that were
532 * reparsed, since positions in those errors can be now stale.
534 let stale_errors =
535 get_files_with_stale_errors
536 ~reparsed
537 ~filter:None
538 ~phases:[Errors.Decl; Errors.Typing]
539 ~errors:env.errorl
541 let to_recheck = Relative_path.Set.union stale_errors to_recheck in
542 let to_recheck = Relative_path.Set.union env.needs_recheck to_recheck in
543 let to_recheck =
544 Relative_path.Set.union
545 (Relative_path.Set.of_list (Relative_path.Map.keys phase_2_decl_defs))
546 to_recheck
548 (to_recheck, Relative_path.Set.empty)
550 let get_env_after_decl ~old_env ~naming_table ~failed_naming =
552 old_env with
553 naming_table;
554 failed_naming;
555 ide_needs_parsing = Relative_path.Set.empty;
556 disk_needs_parsing = Relative_path.Set.empty;
559 let get_env_after_typing
560 ~old_env ~errorl ~needs_phase2_redecl:_ ~needs_recheck ~diag_subscribe =
561 let (full_check, remote) =
562 if Relative_path.Set.is_empty needs_recheck then
563 (Full_check_done, false)
564 else
565 (old_env.full_check, old_env.remote)
567 let needs_full_init =
568 old_env.init_env.needs_full_init && full_check <> Full_check_done
570 let () =
571 log_if_diag_subscribe_changed
572 "get_env[FullCheckKind]"
573 ~before:old_env.diag_subscribe
574 ~after:diag_subscribe
577 old_env with
578 errorl;
579 needs_phase2_redecl = Relative_path.Set.empty;
580 needs_recheck;
581 full_check;
582 remote;
583 init_env = { old_env.init_env with needs_full_init };
584 diag_subscribe;
587 let is_full = true
590 module LazyCheckKind : CheckKindType = struct
591 let get_files_to_parse env = (env.ide_needs_parsing, true)
593 let ide_error_sources env =
594 match env.diag_subscribe with
595 | Some ds -> Diagnostic_subscription.error_sources ds
596 | None -> Relative_path.Set.empty
598 let is_ide_file env x =
599 Relative_path.Set.mem (ide_error_sources env) x
600 || Relative_path.Set.mem env.editor_open_files x
602 let get_defs_to_redecl ~reparsed ~env =
603 (* Same as FullCheckKind.get_defs_to_redecl, but we limit returned set only
604 * to files that are relevant to IDE *)
605 get_files_with_stale_errors
606 ~reparsed
607 ~filter:(Some (ide_error_sources env))
608 ~phases:[Errors.Decl]
609 ~errors:env.errorl
611 let get_defs_to_redecl_phase2
612 genv ~decl_defs ~naming_table ~to_redecl_phase2 ~env =
613 (* Do phase2 only for IDE files, delay the fanout until next full check *)
614 let (to_redecl_phase2_now, to_redecl_phase2_later) =
615 Relative_path.Set.partition (is_ide_file env) to_redecl_phase2
617 ( extend_fast genv decl_defs naming_table to_redecl_phase2_now,
618 extend_fast genv decl_defs naming_table to_redecl_phase2_later )
620 let get_related_files dep =
621 Typing_deps.get_ideps_from_hash dep |> Typing_deps.get_files
623 let get_to_recheck2_approximation ~to_redecl_phase2_deps ~env =
624 (* We didn't do the full fan-out from to_redecl_phase2_deps, so the
625 * to_recheck2 set might not be complete. We would recompute it during next
626 * full check, but if it contains files open in editor, we would like to
627 * recheck them sooner than that. We approximate it by taking all the
628 * possible dependencies of dependencies and preemptively rechecking them
629 * if they are open in the editor *)
630 if Typing_deps.DepSet.cardinal to_redecl_phase2_deps > 1000 then
631 (* inspecting tons of dependencies would take more time that just
632 * rechecking all relevant files. *)
633 Relative_path.Set.union env.editor_open_files (ide_error_sources env)
634 else
635 Typing_deps.DepSet.fold
636 to_redecl_phase2_deps
637 ~init:Relative_path.Set.empty
638 ~f:(fun x acc -> Relative_path.Set.union acc @@ get_related_files x)
639 |> Relative_path.Set.filter ~f:(is_ide_file env)
641 let get_defs_to_recheck ~reparsed ~phase_2_decl_defs ~to_recheck ~env =
642 (* Same as FullCheckKind.get_defs_to_recheck, but we limit returned set only
643 * to files that are relevant to IDE *)
644 let stale_errors =
645 get_files_with_stale_errors
646 ~reparsed
647 ~filter:(Some (ide_error_sources env))
648 ~phases:[Errors.Decl; Errors.Typing]
649 ~errors:env.errorl
651 let to_recheck = Relative_path.Set.union to_recheck stale_errors in
652 let (to_recheck_now, to_recheck_later) =
653 Relative_path.Set.partition (is_ide_file env) to_recheck
655 let to_recheck_now =
656 Relative_path.Set.union
657 (Relative_path.Set.of_list (Relative_path.Map.keys phase_2_decl_defs))
658 to_recheck_now
660 (to_recheck_now, to_recheck_later)
662 let get_env_after_decl ~old_env ~naming_table ~failed_naming =
664 old_env with
665 naming_table;
666 failed_naming;
667 ide_needs_parsing = Relative_path.Set.empty;
670 let get_env_after_typing
671 ~old_env ~errorl ~needs_phase2_redecl ~needs_recheck ~diag_subscribe =
672 (* If it was started, it's still started, otherwise it needs starting *)
673 let full_check =
674 match old_env.full_check with
675 | Full_check_started -> Full_check_started
676 | _ -> Full_check_needed
678 let () =
679 log_if_diag_subscribe_changed
680 "get_env[LazyCheckKind]"
681 ~before:old_env.diag_subscribe
682 ~after:diag_subscribe
685 old_env with
686 errorl;
687 ide_needs_parsing = Relative_path.Set.empty;
688 needs_phase2_redecl;
689 needs_recheck;
690 full_check;
691 diag_subscribe;
694 let is_full = false
697 module Make : functor (CheckKind : CheckKindType) -> sig
698 val type_check_core :
699 ServerEnv.genv -> ServerEnv.env -> ServerEnv.env * check_results
700 end =
701 functor
702 (CheckKind : CheckKindType)
704 struct
705 let get_defs fast =
706 Relative_path.Map.fold
707 fast
709 begin
710 fun _ names1 names2 ->
711 FileInfo.merge_names names1 names2
713 ~init:FileInfo.empty_names
715 let get_oldified_defs env =
716 Relative_path.Set.fold
717 env.needs_phase2_redecl
719 begin
720 fun path acc ->
721 match Naming_table.get_file_info env.naming_table path with
722 | None -> acc
723 | Some names -> FileInfo.(merge_names (simplify names) acc)
725 ~init:FileInfo.empty_names
727 let get_classes naming_table path =
728 match Naming_table.get_file_info naming_table path with
729 | None -> SSet.empty
730 | Some info ->
731 List.fold info.FileInfo.classes ~init:SSet.empty ~f:(fun acc (_, cid) ->
732 SSet.add acc cid)
734 let clear_failed_parsing errors failed_parsing =
735 (* In most cases, set of files processed in a phase is a superset
736 * of files from previous phase - i.e if we run decl on file A, we'll also
737 * run its typing.
738 * In few cases we might choose not to run further stages for files that
739 * failed parsing (see ~stop_at_errors). We need to manually clear out
740 * error lists for those files. *)
741 Relative_path.Set.fold failed_parsing ~init:errors ~f:(fun path acc ->
742 let path = Relative_path.Set.singleton path in
743 List.fold_left
744 Errors.[Naming; Decl; Typing]
745 ~init:acc
747 begin
748 fun acc phase ->
749 Errors.(incremental_update_set acc empty path phase)
750 end)
752 type parsing_result = {
753 parse_errors: Errors.t;
754 failed_parsing: Relative_path.Set.t;
755 fast_parsed: FileInfo.t Relative_path.Map.t;
758 let do_parsing
759 (genv : genv)
760 (env : env)
761 ~(files_to_parse : Relative_path.Set.t)
762 ~(stop_at_errors : bool) : ServerEnv.env * parsing_result =
763 let (env, fast_parsed, errorl, failed_parsing) =
764 parsing genv env files_to_parse ~stop_at_errors
766 let errors = env.errorl in
767 let errors =
768 Errors.(incremental_update_set errors errorl files_to_parse Parsing)
770 let errors = clear_failed_parsing errors failed_parsing in
771 (env, { parse_errors = errors; failed_parsing; fast_parsed })
773 type naming_result = {
774 errors_after_naming: Errors.t;
775 failed_naming: Relative_path.Set.t;
776 fast: Naming_table.fast;
779 let do_naming
780 (genv : genv)
781 (env : env)
782 ~(errors : Errors.t)
783 ~(failed_parsing : Relative_path.Set.t)
784 ~(fast_parsed : FileInfo.t Relative_path.Map.t)
785 ~(naming_table : Naming_table.t)
786 ~(files_to_parse : Relative_path.Set.t)
787 ~(stop_at_errors : bool) : naming_result =
788 let (errorl', failed_naming, fast) = declare_names env fast_parsed in
789 let errors = Errors.(incremental_update_map errors errorl' fast Naming) in
790 (* failed_naming can be a superset of keys in fast - see comment in
791 * Naming_global.ndecl_file *)
792 let fast = extend_fast genv fast naming_table failed_naming in
793 (* COMPUTES WHAT MUST BE REDECLARED *)
794 let failed_decl = CheckKind.get_defs_to_redecl files_to_parse env in
795 let fast = extend_fast genv fast naming_table failed_decl in
796 let fast = add_old_decls env.naming_table fast in
797 let fast =
798 remove_failed_parsing_map fast stop_at_errors env failed_parsing
800 { errors_after_naming = errors; failed_naming; fast }
802 type redecl_phase1_result = {
803 changes: Typing_deps.DepSet.t;
804 oldified_defs: FileInfo.names;
805 to_recheck1: Relative_path.Set.t;
806 to_redecl_phase2_deps: Typing_deps.DepSet.t;
809 let do_redecl_phase1
810 (genv : genv)
811 (env : env)
812 ~(fast : FileInfo.names Relative_path.Map.t)
813 ~(naming_table : Naming_table.t)
814 ~(oldified_defs : FileInfo.names) : redecl_phase1_result =
815 let bucket_size = genv.local_config.SLC.type_decl_bucket_size in
816 let defs_to_redecl = get_defs fast in
817 let ctx = Provider_utils.ctx_from_server_env env in
818 let (_, changes, to_redecl_phase2_deps, to_recheck1) =
819 Decl_redecl_service.redo_type_decl
820 ~conservative_redecl:
821 (not
822 genv.local_config.ServerLocalConfig.disable_conservative_redecl)
823 ~bucket_size
825 genv.workers
826 (get_classes naming_table)
827 oldified_defs
828 fast
830 (* Things that were redeclared are no longer in old heap, so we substract
831 * defs_ro_redecl from oldified_defs *)
832 let oldified_defs =
833 snd @@ Decl_utils.split_defs oldified_defs defs_to_redecl
835 let to_recheck1 = Typing_deps.get_files to_recheck1 in
836 { changes; oldified_defs; to_recheck1; to_redecl_phase2_deps }
838 type redecl_phase2_result = {
839 errors_after_phase2: Errors.t;
840 needs_phase2_redecl: Relative_path.Set.t;
841 to_recheck2: Relative_path.Set.t;
844 let do_redecl_phase2
845 (genv : genv)
846 (env : env)
847 ~(errors : Errors.t)
848 ~(fast_redecl_phase2_now : FileInfo.names Relative_path.Map.t)
849 ~(naming_table : Naming_table.t)
850 ~(lazy_decl_later : FileInfo.names Relative_path.Map.t)
851 ~(oldified_defs : FileInfo.names)
852 ~(to_redecl_phase2_deps : Typing_deps.DepSet.t) : redecl_phase2_result =
853 let ctx = Provider_utils.ctx_from_server_env env in
854 let bucket_size = genv.local_config.SLC.type_decl_bucket_size in
855 let defs_to_oldify = get_defs lazy_decl_later in
856 Decl_redecl_service.oldify_type_decl
858 ~bucket_size
859 genv.workers
860 (get_classes naming_table)
861 oldified_defs
862 defs_to_oldify;
863 let oldified_defs = FileInfo.merge_names oldified_defs defs_to_oldify in
864 let (errorl', _changes, _to_redecl2, to_recheck2) =
865 Decl_redecl_service.redo_type_decl
866 ~conservative_redecl:
867 (not
868 genv.local_config.ServerLocalConfig.disable_conservative_redecl)
869 ~bucket_size
871 genv.workers
872 (get_classes naming_table)
873 oldified_defs
874 fast_redecl_phase2_now
876 let errors =
877 Errors.(
878 incremental_update_map errors errorl' fast_redecl_phase2_now Decl)
880 let needs_phase2_redecl =
881 diff_set_and_map_keys
882 (* Redeclaration delayed before and now. *)
883 (union_set_and_map_keys env.needs_phase2_redecl lazy_decl_later)
884 (* Redeclarations completed now. *)
885 fast_redecl_phase2_now
887 let to_recheck2 = Typing_deps.get_files to_recheck2 in
888 let to_recheck2 =
889 Relative_path.Set.union
890 to_recheck2
891 (CheckKind.get_to_recheck2_approximation to_redecl_phase2_deps env)
893 { errors_after_phase2 = errors; needs_phase2_redecl; to_recheck2 }
895 (** Merge the results of the two redecl phases. *)
896 let merge_redecl_results
897 ~(fast : FileInfo.names Relative_path.Map.t)
898 ~(fast_redecl_phase2_now : FileInfo.names Relative_path.Map.t)
899 ~(to_recheck1 : Relative_path.Set.t)
900 ~(to_recheck2 : Relative_path.Set.t)
901 ~(to_redecl_phase2 : Relative_path.Set.t) :
902 Naming_table.fast * Relative_path.Set.t =
903 let fast = Relative_path.Map.union fast fast_redecl_phase2_now in
904 let to_recheck = Relative_path.Set.union to_recheck1 to_recheck2 in
905 let to_recheck = Relative_path.Set.union to_recheck to_redecl_phase2 in
906 (fast, to_recheck)
908 type type_checking_result = {
909 env: ServerEnv.env;
910 diag_subscribe: Diagnostic_subscription.t option;
911 errors: Errors.t;
912 telemetry: Telemetry.t;
913 files_checked: Relative_path.Set.t;
914 full_check_done: bool;
915 needs_recheck: Relative_path.Set.t;
916 total_rechecked_count: int;
919 let do_type_checking
920 (genv : genv)
921 (env : env)
922 (telemetry : Telemetry.t)
923 ~(errors : Errors.t)
924 ~(files_to_check : Relative_path.Set.t)
925 ~(files_to_parse : Relative_path.Set.t)
926 ~(lazy_check_later : Relative_path.Set.t)
927 ~(old_env : env) : type_checking_result =
928 if Relative_path.(Set.mem files_to_check default) then
929 Hh_logger.log "WARNING: rechecking defintion in a dummy file";
930 let dynamic_view_files =
931 if ServerDynamicView.dynamic_view_on () then
932 env.editor_open_files
933 else
934 Relative_path.Set.empty
936 let interrupt = get_interrupt_config genv env in
937 let memory_cap =
938 genv.local_config.ServerLocalConfig.max_typechecker_worker_memory_mb
940 let fnl = Relative_path.Set.elements files_to_check in
941 let (errorl', delegate_state, telemetry, env', cancelled) =
942 let ctx = Provider_utils.ctx_from_server_env env in
943 Typing_check_service.go_with_interrupt
945 genv.workers
946 env.typing_service.delegate_state
947 telemetry
948 dynamic_view_files
950 ~interrupt
951 ~memory_cap
952 ~check_info:(get_check_info genv env)
954 log_if_diag_subscribe_changed
955 "type_checking.go_with_interrupt"
956 ~before:env.diag_subscribe
957 ~after:env'.diag_subscribe;
958 let env =
960 env' with
961 typing_service = { env'.typing_service with delegate_state };
964 (* Add new things that need to be rechecked *)
965 let needs_recheck =
966 Relative_path.Set.union env.needs_recheck lazy_check_later
968 (* Remove things that were cancelled from things we started rechecking... *)
969 let (files_to_check, needs_recheck) =
970 List.fold
971 cancelled
972 ~init:(files_to_check, needs_recheck)
973 ~f:(fun (files_to_check, needs_recheck) path ->
974 ( Relative_path.Set.remove files_to_check path,
975 Relative_path.Set.add needs_recheck path ))
977 (* ... leaving only things that we actually checked, and which can be
978 * removed from needs_recheck *)
979 let needs_recheck = Relative_path.Set.diff needs_recheck files_to_check in
980 let errors =
981 Errors.(incremental_update_set errors errorl' files_to_check Typing)
983 let full_check_done =
984 CheckKind.is_full && Relative_path.Set.is_empty needs_recheck
986 let diag_subscribe =
987 Option.map env.diag_subscribe ~f:(fun x ->
988 Diagnostic_subscription.update
990 ~priority_files:env.editor_open_files
991 ~reparsed:files_to_parse
992 ~rechecked:files_to_check
993 ~global_errors:errors
994 ~full_check_done)
996 log_if_diag_subscribe_changed
997 "type_checking[old_env->env]"
998 ~before:old_env.diag_subscribe
999 ~after:env.diag_subscribe;
1000 log_if_diag_subscribe_changed
1001 "type_checking[env->diag_subscribe]"
1002 ~before:env.diag_subscribe
1003 ~after:diag_subscribe;
1004 log_if_diag_subscribe_changed
1005 "type_checking[old_env->diag_subscribe]"
1006 ~before:old_env.diag_subscribe
1007 ~after:diag_subscribe;
1009 let total_rechecked_count = Relative_path.Set.cardinal files_to_check in
1011 env;
1012 diag_subscribe;
1013 errors;
1014 telemetry;
1015 files_checked = files_to_check;
1016 full_check_done;
1017 needs_recheck;
1018 total_rechecked_count;
1021 let type_check_core genv env =
1022 let env =
1023 if CheckKind.is_full then
1024 { env with full_check = Full_check_started }
1025 else
1028 let start_t = Unix.gettimeofday () in
1029 let t = start_t in
1030 (* Files in env.needs_decl contain declarations which were not finished.
1031 * They were only oldified, but we didn't run phase2 redeclarations for them
1032 * which would compute new versions, compare them with old ones and remove
1033 * the old ones. We'll use oldified_defs sets to track what is in the old
1034 * heap as we progress with redeclaration *)
1035 let oldified_defs = get_oldified_defs env in
1036 let (files_to_parse, stop_at_errors) = CheckKind.get_files_to_parse env in
1037 let reparse_count = Relative_path.Set.cardinal files_to_parse in
1038 if reparse_count = 1 then
1039 files_to_parse
1040 |> Relative_path.Set.choose
1041 |> Relative_path.to_absolute
1042 |> Hh_logger.log "Processing changes to 1 file: %s"
1043 else
1044 Hh_logger.log "Processing changes to %d files" reparse_count;
1046 if CheckKind.is_full then (
1047 let redecl_count = Relative_path.Set.cardinal env.needs_phase2_redecl in
1048 let check_count = Relative_path.Set.cardinal env.needs_recheck in
1049 Hh_logger.log
1050 "Processing deferred type decl for %d file%s"
1051 redecl_count
1052 ( if redecl_count = 1 then
1054 else
1055 "s" );
1056 Hh_logger.log
1057 "Processing deferred typechecking for %d file%s"
1058 check_count
1059 ( if check_count = 1 then
1061 else
1062 "s" )
1065 (* PARSING ***************************************************************)
1066 debug_print_path_set genv "files_to_parse" files_to_parse;
1068 ServerProgress.send_progress_to_monitor
1069 ~include_in_logs:false
1070 "parsing %d files"
1071 reparse_count;
1072 let logstring = Printf.sprintf "Parsing %d files" reparse_count in
1073 Hh_logger.log "Begin %s" logstring;
1075 (* Parse all changed files. *)
1076 let (env, { parse_errors = errors; failed_parsing; fast_parsed }) =
1077 do_parsing genv env ~files_to_parse ~stop_at_errors
1079 let hs = SharedMem.heap_size () in
1080 HackEventLogger.parsing_end t hs ~parsed_count:reparse_count;
1081 let t = Hh_logger.log_duration logstring t in
1082 Hh_logger.log "Heap size: %d" hs;
1084 (* UPDATE NAMING TABLES **************************************************)
1085 let logstring = "Updating deps" in
1086 Hh_logger.log "Begin %s" logstring;
1088 (* Hold on to the original environment; it's used by do_type_checking. *)
1089 let old_env = env in
1090 (* Update the naming_table, which is a map from filename to the names of
1091 toplevel symbols declared in that file. Also, update Typing_deps' table,
1092 which is a map from toplevel symbol hash (Dep.t) to filename. *)
1093 let naming_table = update_naming_table env fast_parsed in
1094 HackEventLogger.updating_deps_end t;
1095 let t = Hh_logger.log_duration logstring t in
1096 (* NAMING ****************************************************************)
1097 ServerProgress.send_progress_to_monitor
1098 ~include_in_logs:false
1099 "resolving symbol references";
1100 let logstring = "Naming" in
1101 Hh_logger.log "Begin %s" logstring;
1103 let deptable_unlocked = Typing_deps.allow_dependency_table_reads true in
1104 (* Run Naming_global, updating the reverse naming table (which maps the names
1105 of toplevel symbols to the files in which they were declared) in shared
1106 memory. Does not run Naming itself (which converts an AST to a NAST by
1107 assigning unique identifiers to locals, among other things). The Naming
1108 module is something of a historical artifact and is slated for removal,
1109 but for now, it is run immediately before typechecking. *)
1110 let { errors_after_naming = errors; failed_naming; fast } =
1111 do_naming
1112 genv
1114 ~errors
1115 ~failed_parsing
1116 ~fast_parsed
1117 ~naming_table
1118 ~files_to_parse
1119 ~stop_at_errors
1121 HackEventLogger.naming_end t;
1122 let t = Hh_logger.log_duration logstring t in
1123 (* REDECL PHASE 1 ********************************************************)
1124 ServerProgress.send_progress_to_monitor
1125 ~include_in_logs:false
1126 "determining changes";
1127 let count = Relative_path.Map.cardinal fast in
1128 let logstring =
1129 Printf.sprintf "Type declaration (phase 1) for %d files" count
1131 Hh_logger.log "Begin %s" logstring;
1132 Hh_logger.log
1133 "(Recomputing type declarations in changed files and determining immediate typechecking fanout)";
1135 debug_print_fast_keys genv "to_redecl_phase1" fast;
1137 (* Do phase 1 of redeclaration. Here we compare the old and new versions of
1138 the declarations defined in all changed files, and collect the set of
1139 files which need to be re-typechecked as a consequence of those changes,
1140 as well as the set of files whose folded class declarations must be
1141 recomputed as a consequence of those changes (in phase 2).
1143 When shallow_class_decl is enabled, there is no need to do phase 2--the
1144 only source of class information needing recomputing is linearizations.
1145 These are invalidated by Decl_redecl_service.redo_type_decl in phase 1,
1146 and are lazily recomputed as needed. *)
1147 let { changes; oldified_defs; to_recheck1; to_redecl_phase2_deps } =
1148 do_redecl_phase1 genv env ~fast ~naming_table ~oldified_defs
1150 let to_redecl_phase2 = Typing_deps.get_files to_redecl_phase2_deps in
1151 let hs = SharedMem.heap_size () in
1152 HackEventLogger.first_redecl_end t hs;
1153 let t = Hh_logger.log_duration logstring t in
1154 Hh_logger.log "Heap size: %d" hs;
1156 ServerRevisionTracker.decl_changed
1157 genv.ServerEnv.local_config
1158 (Relative_path.Set.cardinal to_redecl_phase2);
1160 (* REDECL PHASE 2 ********************************************************)
1162 (* For a full typecheck, we want to redeclare everything that needs
1163 redeclaration (either because it was invalidated in phase 1, or because
1164 it was invalidated by a previous lazy check). For a lazy check, we only
1165 want to redeclare files open in the IDE, leaving everything else to be
1166 lazily redeclared later. In either case, there's no need to attempt to
1167 redeclare definitions in files with parse errors.
1169 When shallow_class_decl is enabled, there is no need to do phase 2. *)
1170 let ctx = Provider_utils.ctx_from_server_env env in
1171 let (fast_redecl_phase2_now, lazy_decl_later) =
1172 if shallow_decl_enabled ctx then
1173 (Relative_path.Map.empty, Relative_path.Map.empty)
1174 else
1175 CheckKind.get_defs_to_redecl_phase2
1176 genv
1177 fast
1178 naming_table
1179 to_redecl_phase2
1182 let fast_redecl_phase2_now =
1183 remove_failed_parsing_map
1184 fast_redecl_phase2_now
1185 stop_at_errors
1187 failed_parsing
1189 let count = Relative_path.Map.cardinal fast_redecl_phase2_now in
1190 ServerProgress.send_progress_to_monitor
1191 ~include_in_logs:false
1192 "evaluating type declarations of %d files"
1193 count;
1194 let logstring =
1195 Printf.sprintf "Type declaration (phase 2) for %d files" count
1197 Hh_logger.log "Begin %s" logstring;
1199 if not (shallow_decl_enabled ctx) then (
1200 Hh_logger.log
1201 "(Recomputing type declarations for descendants of changed classes and determining full typechecking fanout)";
1202 Hh_logger.log
1203 "Invalidating (but not recomputing) declarations in %d files"
1204 (Relative_path.Map.cardinal lazy_decl_later)
1207 debug_print_fast_keys genv "to_redecl_phase2" fast_redecl_phase2_now;
1208 debug_print_fast_keys genv "lazy_decl_later" lazy_decl_later;
1210 (* Redeclare the set of files whose folded class decls needed to be
1211 recomputed as a result of phase 1. Collect the set of files which need to
1212 be re-typechecked because of changes between the old and new
1213 declarations. We need not collect a set of files to redeclare (again)
1214 because our to_redecl set from phase 1 included the transitive children
1215 of changed classes.
1217 When shallow_class_decl is enabled, there is no need to do phase 2. *)
1218 let (errors, needs_phase2_redecl, to_recheck2) =
1219 if shallow_decl_enabled ctx then
1220 (errors, Relative_path.Set.empty, Relative_path.Set.empty)
1221 else
1222 let { errors_after_phase2; needs_phase2_redecl; to_recheck2 } =
1223 do_redecl_phase2
1224 genv
1226 ~errors
1227 ~fast_redecl_phase2_now
1228 ~naming_table
1229 ~lazy_decl_later
1230 ~oldified_defs
1231 ~to_redecl_phase2_deps
1233 (errors_after_phase2, needs_phase2_redecl, to_recheck2)
1235 (* We have changed declarations, which means that typed ASTs could have
1236 * changed too. *)
1237 Ide_tast_cache.invalidate ();
1239 let (fast, to_recheck) =
1240 merge_redecl_results
1241 ~fast
1242 ~fast_redecl_phase2_now
1243 ~to_recheck1
1244 ~to_recheck2
1245 ~to_redecl_phase2
1247 let hs = SharedMem.heap_size () in
1248 HackEventLogger.second_redecl_end t hs;
1249 let t = Hh_logger.log_duration logstring t in
1250 Hh_logger.log "Heap size: %d" hs;
1252 ServerRevisionTracker.typing_changed
1253 genv.local_config
1254 (Relative_path.Set.cardinal to_recheck);
1256 let env =
1257 CheckKind.get_env_after_decl ~old_env:env ~naming_table ~failed_naming
1259 (* HANDLE PRECHECKED FILES AFTER LOCAL CHANGES ***************************)
1260 Hh_logger.log "Begin evaluating prechecked changes";
1261 let env =
1262 ServerPrecheckedFiles.update_after_local_changes genv env changes
1264 let t = Hh_logger.log_duration "Evaluating prechecked changes" t in
1265 let (_ : bool) =
1266 Typing_deps.allow_dependency_table_reads deptable_unlocked
1268 (* Checking this before starting typechecking because we want to attribtue
1269 * big rechecks to rebases, even when restarting is disabled *)
1271 genv.local_config.ServerLocalConfig.hg_aware_recheck_restart_threshold
1273 then
1274 ServerRevisionTracker.check_blocking ();
1276 (* TYPE CHECKING *********************************************************)
1278 (* For a full check, typecheck everything which may be affected by the
1279 changes. For a lazy check, typecheck only the affected files which are
1280 open in the IDE, leaving other affected files to be lazily checked later.
1281 In either case, don't attempt to typecheck files with parse errors. *)
1282 let (files_to_check, lazy_check_later) =
1283 CheckKind.get_defs_to_recheck files_to_parse fast to_recheck env
1285 let (should_start_delegate, t) =
1286 ServerCheckUtils.should_do_remote env.tcopt files_to_check ~t
1288 let env =
1289 if should_start_delegate then
1290 start_typing_delegate genv env
1291 else
1294 let files_to_check =
1295 remove_failed_parsing_set
1296 files_to_check
1297 stop_at_errors
1299 failed_parsing
1301 let to_recheck_count = Relative_path.Set.cardinal files_to_check in
1302 ServerProgress.send_progress_to_monitor
1303 ~include_in_logs:false
1304 "typechecking %d files"
1305 to_recheck_count;
1306 let logstring = Printf.sprintf "Typechecking %d files" in
1307 Hh_logger.log "Begin %s" (logstring to_recheck_count);
1309 debug_print_path_set genv "to_recheck" files_to_check;
1310 debug_print_path_set genv "lazy_check_later" lazy_check_later;
1312 ServerCheckpoint.process_updates files_to_check;
1314 (* Typecheck all of the files we determined might need rechecking as a
1315 consequence of the changes (or, in a lazy check, the subset of those
1316 files which are open in an IDE buffer). *)
1317 let telemetry = Telemetry.create () in
1318 let {
1319 env;
1320 diag_subscribe;
1321 errors;
1322 telemetry;
1323 files_checked;
1324 full_check_done;
1325 needs_recheck;
1326 total_rechecked_count;
1328 do_type_checking
1329 genv
1331 telemetry
1332 ~errors
1333 ~files_to_check
1334 ~files_to_parse
1335 ~lazy_check_later
1336 ~old_env
1338 log_if_diag_subscribe_changed
1339 "type_check_core[old_env->env]"
1340 ~before:old_env.diag_subscribe
1341 ~after:env.diag_subscribe;
1342 log_if_diag_subscribe_changed
1343 "type_check_core.[env->diag_subscribe]"
1344 ~before:env.diag_subscribe
1345 ~after:diag_subscribe;
1346 log_if_diag_subscribe_changed
1347 "type_check_core[old_env->diag_subscribe]"
1348 ~before:old_env.diag_subscribe
1349 ~after:diag_subscribe;
1351 HackEventLogger.type_check_end
1352 telemetry
1353 ~started_count:to_recheck_count
1354 ~count:total_rechecked_count
1355 ~experiments:genv.local_config.ServerLocalConfig.experiments
1356 ~start_t:t;
1357 let logstring =
1358 Printf.sprintf "Typechecked %d files" total_rechecked_count
1360 let t = Hh_logger.log_duration logstring t in
1361 let hs = SharedMem.heap_size () in
1362 Hh_logger.log "Heap size: %d" hs;
1364 Hh_logger.log "Total: %f\n%!" (t -. start_t);
1366 (* INVALIDATE FILES (EXPERIMENTAL TYPES IN CODEGEN) **********************)
1367 ServerInvalidateUnits.go genv files_checked fast_parsed naming_table;
1369 let env =
1370 CheckKind.get_env_after_typing
1372 errors
1373 needs_phase2_redecl
1374 needs_recheck
1375 diag_subscribe
1377 (* STATS LOGGING *********************************************************)
1379 SharedMem.hh_log_level () > 0
1380 || GlobalOptions.tco_language_feature_logging env.tcopt
1381 then (
1382 Measure.print_stats ();
1383 Measure.print_distributions ();
1385 (* Log lambda counts for full checks where we don't load from a saved state *)
1387 genv.ServerEnv.options |> ServerArgs.no_load
1388 && full_check_done
1389 && reparse_count = 0
1390 (* Ignore incremental updates *)
1391 then
1392 TypingLogger.log_lambda_counts ()
1394 ServerDebug.info genv "incremental_done";
1396 (* HANDLE PRECHECKED FILES AFTER RECHECK *********************************)
1397 let deptable_unlocked = Typing_deps.allow_dependency_table_reads true in
1398 let env =
1399 ServerPrecheckedFiles.update_after_recheck genv env files_checked
1401 let (_ : bool) =
1402 Typing_deps.allow_dependency_table_reads deptable_unlocked
1404 (* We might have completed a full check, which might mean that a rebase was
1405 * successfully processed. *)
1406 ServerRevisionTracker.check_non_blocking env;
1408 let env =
1410 env with
1411 typing_service =
1413 env.typing_service with
1414 delegate_state =
1415 Typing_service_delegate.stop env.typing_service.delegate_state;
1420 (env, { reparse_count; total_rechecked_count })
1423 let check_kind_to_string = function
1424 | Full_check -> "Full_check"
1425 | Lazy_check -> "Lazy_check"
1427 module FC = Make (FullCheckKind)
1428 module LC = Make (LazyCheckKind)
1430 let type_check_unsafe genv env kind =
1431 (match kind with
1432 | Lazy_check -> HackEventLogger.set_lazy_incremental ()
1433 | Full_check -> ());
1434 let check_kind = check_kind_to_string kind in
1435 HackEventLogger.with_check_kind check_kind @@ fun () ->
1436 Printf.eprintf "******************************************\n";
1437 Hh_logger.log "Check kind: %s" check_kind;
1438 match kind with
1439 | Lazy_check ->
1440 ServerBusyStatus.send env ServerCommandTypes.Doing_local_typecheck;
1441 let res = LC.type_check_core genv env in
1442 ServerBusyStatus.send env ServerCommandTypes.Done_local_typecheck;
1444 | Full_check ->
1445 ServerBusyStatus.send
1447 (ServerCommandTypes.Doing_global_typecheck
1448 (global_typecheck_kind genv env));
1449 let ((env, _) as res) = FC.type_check_core genv env in
1450 ( if env.full_check = Full_check_done then
1451 let total = Errors.count env.ServerEnv.errorl in
1452 let (is_truncated, shown) =
1453 match env.ServerEnv.diag_subscribe with
1454 | None -> (false, 0)
1455 | Some ds -> Diagnostic_subscription.get_pushed_error_length ds
1457 let msg =
1458 ServerCommandTypes.Done_global_typecheck { is_truncated; shown; total }
1460 ServerBusyStatus.send env msg );
1463 let type_check genv env kind =
1464 ServerUtils.with_exit_on_exception @@ fun () ->
1465 type_check_unsafe genv env kind