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.
12 (*****************************************************************************)
13 (* Recheck loop types. *)
14 (*****************************************************************************)
16 type recheck_loop_stats
= {
17 (** Watchman subscription has gone down, so state of the world after the
18 * recheck loop may not reflect what is actually on disk. *)
20 rechecked_batches
: int;
21 rechecked_count
: int;
22 (* includes dependencies *)
23 total_rechecked_count
: int;
26 let empty_recheck_loop_stats = {
27 updates_stale
= false;
28 rechecked_batches
= 0;
30 total_rechecked_count
= 0;
33 (*****************************************************************************)
34 (* The "static" environment, initialized first and then doesn't change *)
35 (*****************************************************************************)
38 options
: ServerArgs.options
;
39 config
: ServerConfig.t
;
40 local_config
: ServerLocalConfig.t
;
41 workers
: MultiWorker.worker list
option;
42 (* Returns the list of files under .hhconfig, subject to a filter *)
43 indexer
: (string -> bool) -> (unit -> string list
);
44 (* Each time this is called, it should return the files that have changed
45 * since the last invocation *)
46 notifier_async
: unit -> ServerNotifierTypes.notifier_changes
;
47 (* If this FD is readable, next call to notifier_async () should read
48 * something from it. *)
49 notifier_async_reader
: unit -> Buffered_line_reader.t
option;
50 notifier
: unit -> SSet.t
;
51 (* If daemons are spawned as part of the init process, wait for them here
52 * e.g. wait until dfindlib is ready (in the case that watchman is absent) *)
53 wait_until_ready
: unit -> unit;
54 mutable debug_channels
: (Timeout.in_channel
* out_channel
) option;
57 (*****************************************************************************)
58 (* The environment constantly maintained by the server *)
59 (*****************************************************************************)
61 type full_check_status
=
62 (* Some updates have not been fully processed. We get into this state every
63 * time file contents change (on disk, or through IDE notifications).
64 * Operations that depend on global state (like taking full error list, or
65 * looking up things in dependency table) will have stale results. *)
67 (* Same as above, except server will actively try to process outstanding
68 * changes (by going into ServerTypeCheck from main loop - this might need to
69 * be repeated several times before progressing to Full_check_done, due to
70 * ability to interrupt typecheck jobs).
71 * Server starts in this state, and we also enter it from Full_check_needed
72 * whenever there is a command requiring full check pending, or when user
75 (* All the changes have been fully processed. *)
78 (* In addition to this environment, many functions are storing and
79 * updating ASTs, NASTs, and types in a shared space
80 * (see respectively Parser_heap, Naming_heap, Typing_env).
81 * The Ast.id are keys to index this shared space.
84 files_info
: FileInfo.t
Relative_path.Map.t
;
85 tcopt
: TypecheckerOptions.t
;
86 popt
: ParserOptions.t
;
87 (* Errors are indexed by files that were known to GENERATE errors in
88 * corresponding phases. Note that this is different from HAVING errors -
89 * it's possible for checking of A to generate error in B - in this case
90 * Errors.get_failed_files Typing should contain A, not B.
91 * Conversly, if declaring A will require declaring B, we should put
92 * B in failed decl. Same if checking A will cause declaring B (via lazy
95 * During recheck, we add those files to the set of files to reanalyze
96 * at each stage in order to regenerate their error lists. So those
97 * failed_ sets are the main piece of mutable state that incremental mode
98 * needs to maintain - the errors themselves are more of a cache, and should
99 * always be possible to be regenerated based on those sets. *)
101 (* failed_naming is used as kind of a dependency tracking mechanism:
102 * if files A.php and B.php both define class C, then those files are
103 * mutually depending on each other (edit to one might resolve naming
104 * ambiguity and change the interpretation of the other). Both of those
105 * files being inside failed_naming is how we track the need to
106 * check for this condition.
108 * See test_naming_errors.ml and test_failed_naming.ml
110 failed_naming
: Relative_path.Set.t
;
111 persistent_client
: ClientProvider.client
option;
112 (* Whether last received IDE command was IDE_IDLE *)
114 (* Timestamp of last IDE file synchronization command *)
115 last_command_time
: float;
116 (* Timestamp of last query for disk changes *)
117 last_notifier_check_time
: float;
118 (* Timestamp of last ServerIdle.go run *)
119 last_idle_job_time
: float;
120 (* The map from full path to synchronized file contents *)
121 editor_open_files
: Relative_path.Set.t
;
122 (* Files which parse trees were invalidated (because they changed on disk
123 * or in editor) and need to be re-parsed *)
124 ide_needs_parsing
: Relative_path.Set.t
;
125 disk_needs_parsing
: Relative_path.Set.t
;
126 (* Declarations that became invalidated and moved to "old" part of the heap.
127 * We keep them there to be used in "determining changes" step of recheck.
128 * (when they are compared to "new" versions). Depending on lazy decl to
129 * compute "new" versions in all the other scenarios (like IDE queries) *)
130 needs_phase2_redecl
: Relative_path.Set.t
;
131 (* Files that need to be typechecked before commands that depend on global
132 * state (like full list of errors, build, or find all references) can be
133 * executed . After full check this should be empty, unless that check was
134 * cancelled mid-flight, in which case full_check will be set to
135 * Full_check_started and entire thing will be retried on next iteration. *)
136 needs_recheck
: Relative_path.Set.t
;
138 full_check
: full_check_status
;
139 prechecked_files
: prechecked_files_status
;
140 (* Not every caller of rechecks expects that they can be interrupted,
141 * so making it opt-in by setting this flag at call site *)
142 can_interrupt
: bool;
143 interrupt_handlers
: genv
-> env
->
144 (Unix.file_descr
* env
MultiThreadedCall.interrupt_handler
) list
;
145 (* When persistent client sends a command that cannot be handled (due to
146 * thread safety) we put the continuation that finishes handling it here. *)
147 pending_command_needs_writes
: (env
-> env
) option;
148 (* When persistent client sends a command that cannot be immediately handled
149 * (due to needing full check) we put the continuation that finishes handling
150 * it here. The string specifies a reason why this command needs full
151 * recheck (for logging/debugging purposes) *)
152 persistent_client_pending_command_needs_full_check
:
153 ((env
-> env
) * string) option;
154 (* Same as above, but for non-persistent clients *)
155 default_client_pending_command_needs_full_check
:
156 ((env
-> env
) * string * ClientProvider.client
) option;
157 (* The diagnostic subscription information of the current client *)
158 diag_subscribe
: Diagnostic_subscription.t
option;
159 recent_recheck_loop_stats
: recheck_loop_stats
;
163 (* We are rechecking dirty files to bootstrap the dependency graph.
164 * After this is done we need to also recheck full fan-out (in this updated
165 * graph) of provided set. *)
166 dirty_local_deps
: Typing_deps.DepSet.t
;
167 (* The fan-outs of those nodes were not expanded yet. *)
168 dirty_master_deps
: Typing_deps.DepSet.t
;
169 (* Files that have been rechecked since server startup *)
170 rechecked_files
: Relative_path.Set.t
;
171 (* Those deps have already been checked against their interaction with
172 * dirty_master_deps. Storing them here to avoid checking it over and over *)
173 clean_local_deps
: Typing_deps.DepSet.t
;
176 (* When using prechecked files we split initial typechecking in two phases
177 * (dirty files and a subset of their fan-out). Other init types compute the
178 * full fan-out up-front. *)
179 and prechecked_files_status
=
180 | Prechecked_files_disabled
181 | Initial_typechecking
of dirty_deps
182 | Prechecked_files_ready
of dirty_deps
185 init_start_t
: float;
186 (* Whether a full check was ever completed since init. *)
187 needs_full_init
: bool;
188 (* Additional data associated with init that we want to log when a first full
189 * check completes. *)
190 state_distance
: int option;
191 approach_name
: string;
192 init_error
: string option;
196 let list_files env oc
=
197 let acc = List.fold_right
198 ~f
:begin fun error
acc ->
199 let pos = Errors.get_pos error
in
200 Relative_path.Set.add
acc (Pos.filename
pos)
202 ~init
:Relative_path.Set.empty
203 (Errors.get_error_list env
.errorl
) in
204 Relative_path.Set.iter
acc (fun s
->
205 Printf.fprintf oc
"%s\n" (Relative_path.to_absolute s
));