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