sync the repo
[hiphop-php.git] / hphp / hack / src / server / serverRpc.ml
bloba52cd0ddfc4140c01324e0bc7e8e458d8edbec56
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 Hh_prelude
11 open ServerEnv
12 open ServerCommandTypes
14 let remove_dead_warning name =
15 "hh_server was started without '--no-load', which is required when removing dead "
16 ^ name
17 ^ "s.\n"
18 ^ "Please run 'hh_client restart --no-load' to restart it."
20 let take_max_errors max_errors error_list =
21 match max_errors with
22 | Some max_errors ->
23 let (error_list, dropped_errors) = List.split_n error_list max_errors in
24 (error_list, List.length dropped_errors)
25 | None -> (error_list, 0)
27 let single_ctx env path file_input =
28 let contents =
29 match file_input with
30 | ServerCommandTypes.FileName path -> Sys_utils.cat path
31 | ServerCommandTypes.FileContent contents -> contents
33 let ctx = Provider_utils.ctx_from_server_env env in
34 Provider_context.add_or_overwrite_entry_contents ~ctx ~path ~contents
36 let single_ctx_path env path =
37 single_ctx
38 env
39 (Relative_path.create_detect_prefix path)
40 (ServerCommandTypes.FileName path)
42 let log_check_response env =
43 HackEventLogger.check_response
44 (Errors.get_error_list env.errorl
45 |> List.map ~f:(fun { User_error.code; _ } -> code))
47 (* Might raise {!Naming_table.File_info_not_found} *)
48 let handle : type a. genv -> env -> is_stale:bool -> a t -> env * a =
49 fun genv env ~is_stale -> function
50 | STATUS { max_errors; _ } ->
51 log_check_response env;
52 let error_list = Errors.sort_and_finalize env.errorl in
53 let (error_list, dropped_count) = take_max_errors max_errors error_list in
54 let liveness =
55 if is_stale then
56 Stale_status
57 else
58 Live_status
60 let last_recheck_stats =
61 match env.ServerEnv.last_recheck_loop_stats_for_actual_work with
62 | None -> None
63 | Some recheck_stats ->
64 Some
65 (ServerEnv.RecheckLoopStats.to_user_telemetry recheck_stats
66 |> Telemetry.string_ ~key:"init_id" ~value:env.init_env.init_id)
68 ( env,
69 { Server_status.liveness; error_list; dropped_count; last_recheck_stats }
71 | STATUS_SINGLE
72 { file_names; max_errors; preexisting_warnings; return_expanded_tast } ->
73 let ctx = Provider_utils.ctx_from_server_env env in
74 let (errors, tasts) = ServerStatusSingle.go file_names ctx in
75 let errors =
76 errors
77 |> ServerTypeCheck.filter_out_mergebase_warnings env ~preexisting_warnings
78 |> Errors.get_sorted_error_list
79 |> List.map ~f:User_error.to_absolute
80 |> take_max_errors max_errors
82 (* Unforced lazy values are closures which make serialization over RPC fail. *)
83 let tasts =
84 if return_expanded_tast then
85 Some
86 (Relative_path.Map.map
87 tasts
88 ~f:
89 (Tast_with_dynamic.map ~f:(fun tast ->
90 tast
91 |> Tast_expand.expand_program ctx
92 |> Tast.force_lazy_values)))
93 else
94 None
96 (env, (errors, tasts))
97 | INFER_TYPE (file_input, line, column) ->
98 let path =
99 match file_input with
100 | FileName fn -> Relative_path.create_detect_prefix fn
101 | FileContent _ -> Relative_path.create_detect_prefix ""
103 let (ctx, entry) = single_ctx env path file_input in
104 let result =
105 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
106 ServerInferType.go_ctx ~ctx ~entry ~line ~column)
108 (env, result)
109 | INFER_TYPE_BATCH positions ->
110 (env, ServerInferTypeBatch.go genv.workers positions env)
111 | IS_SUBTYPE stdin -> (env, ServerIsSubtype.check genv.workers stdin env)
112 | TAST_HOLES (file_input, hole_filter) ->
113 let path =
114 match file_input with
115 | FileName fn -> Relative_path.create_detect_prefix fn
116 | FileContent _ -> Relative_path.create_detect_prefix ""
118 let (ctx, entry) = single_ctx env path file_input in
119 let result =
120 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
121 ServerCollectTastHoles.go_ctx ~ctx ~entry ~hole_filter)
123 (env, result)
124 | TAST_HOLES_BATCH files ->
125 (env, ServerTastHolesBatch.go genv.workers files env)
126 | INFER_TYPE_ERROR (file_input, line, column) ->
127 let path =
128 match file_input with
129 | FileName fn -> Relative_path.create_detect_prefix fn
130 | FileContent _ -> Relative_path.create_detect_prefix ""
132 let (ctx, entry) = single_ctx env path file_input in
133 let result =
134 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
135 ServerInferTypeError.go_ctx ~ctx ~entry ~line ~column)
137 (env, result)
138 (* TODO: edit this to look for classname *)
139 | XHP_AUTOCOMPLETE_SNIPPET cls ->
140 let ctx = Provider_utils.ctx_from_server_env env in
141 let tast_env = Tast_env.empty ctx in
142 let cls = Utils.add_ns cls in
143 (env, AutocompleteService.get_snippet_for_xhp_classname cls ctx tast_env)
144 | IDENTIFY_SYMBOL arg ->
145 let module SO = SymbolOccurrence in
146 let ctx = Provider_utils.ctx_from_server_env env in
147 let get_def_opt type_ name =
148 ServerSymbolDefinition.go
150 None
151 SO.{ type_; name; is_declaration = false; pos = Pos.none }
152 |> Option.to_list
153 |> List.map ~f:SymbolDefinition.to_absolute
155 let arg = Str.split (Str.regexp "::") arg in
156 (* The following are all the different named entities I could think of in Hack. *)
157 let results =
158 match arg with
159 | [c_name; member] ->
160 let c_name = Utils.add_ns c_name in
161 List.concat
163 get_def_opt (SO.Method (SO.ClassName c_name, member)) "";
164 get_def_opt (SO.Property (SO.ClassName c_name, member)) "";
165 get_def_opt (SO.XhpLiteralAttr (c_name, member)) "";
166 get_def_opt (SO.ClassConst (SO.ClassName c_name, member)) "";
167 get_def_opt (SO.Typeconst (c_name, member)) "";
169 | [name] ->
170 let name = Utils.add_ns name in
171 List.concat
173 get_def_opt (SO.Class SO.ClassId) name;
174 (* SO.Record and Class find the same things *)
175 get_def_opt SO.Function name;
176 get_def_opt SO.GConst name;
178 | _ -> []
180 (env, results)
181 | IDENTIFY_FUNCTION (filename, file_input, line, column) ->
182 let (ctx, entry) =
183 single_ctx env (Relative_path.create_detect_prefix filename) file_input
185 let result =
186 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
187 ServerIdentifyFunction.go_quarantined_absolute
188 ~ctx
189 ~entry
190 ~line
191 ~column)
193 (env, result)
194 | METHOD_JUMP (class_, filter, find_children) ->
195 Printf.printf "%s" class_;
196 let ctx = Provider_utils.ctx_from_server_env env in
197 ( env,
198 MethodJumps.get_inheritance
200 class_
201 ~filter
202 ~find_children
203 env.naming_table
204 genv.workers )
205 | METHOD_JUMP_BATCH (classes, filter) ->
206 let ctx = Provider_utils.ctx_from_server_env env in
207 (env, ServerMethodJumpsBatch.go ctx genv.workers classes filter)
208 | FIND_REFS find_refs_action ->
209 let ctx = Provider_utils.ctx_from_server_env env in
210 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
211 let open Done_or_retry in
212 let include_defs = false in
213 ServerFindRefs.(
216 find_refs_action
217 include_defs
218 ~stream_file:None
219 ~hints:[]
220 genv
222 |> map_env ~f:to_absolute))
223 | GO_TO_IMPL go_to_impl_action ->
224 Done_or_retry.(
225 ServerGoToImpl.go ~action:go_to_impl_action ~genv ~env
226 |> map_env ~f:ServerFindRefs.to_absolute)
227 | IDE_FIND_REFS_BY_SYMBOL
229 FindRefsWireFormat.CliArgs.symbol_name = _;
230 action;
231 stream_file;
232 hint_suffixes;
233 } ->
234 let hints =
235 List.map hint_suffixes ~f:(fun suffix -> Relative_path.from_root ~suffix)
237 let ctx = Provider_utils.ctx_from_server_env env in
238 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
239 let open Done_or_retry in
240 let include_defs = false in
241 map_env
242 ~f:ServerFindRefs.to_absolute
243 (ServerFindRefs.go
245 action
246 include_defs
247 ~stream_file
248 ~hints
249 genv
250 env))
251 | IDE_GO_TO_IMPL_BY_SYMBOL
252 { FindRefsWireFormat.CliArgs.symbol_name = _; action; _ } ->
253 let ctx = Provider_utils.ctx_from_server_env env in
254 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
255 let open Done_or_retry in
256 map_env
257 ~f:ServerFindRefs.to_absolute
258 (ServerGoToImpl.go ~action ~genv ~env))
259 | RENAME rename_action ->
260 let ctx = Provider_utils.ctx_from_server_env env in
261 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
262 let definition_for_wrapper =
263 match rename_action with
264 | ServerRenameTypes.ClassRename _
265 | ServerRenameTypes.ClassConstRename _
266 | ServerRenameTypes.LocalVarRename _ ->
267 None
268 | ServerRenameTypes.MethodRename { class_name; old_name; _ } ->
269 ServerSymbolDefinition.go
271 None
273 SymbolOccurrence.name = "unused for lookup";
274 type_ =
275 SymbolOccurrence.Method
276 ( SymbolOccurrence.ClassName (Utils.add_ns class_name),
277 old_name );
278 is_declaration = false;
279 pos = Pos.none;
281 | ServerRenameTypes.FunctionRename { old_name; _ } ->
282 ServerSymbolDefinition.go
284 None
286 SymbolOccurrence.name = Utils.add_ns old_name;
287 type_ = SymbolOccurrence.Function;
288 is_declaration = false;
289 pos = Pos.none;
292 ServerRename.go ctx rename_action genv env ~definition_for_wrapper)
293 | IDE_RENAME_BY_SYMBOL (action, new_name, symbol_definition) ->
294 let ctx = Provider_utils.ctx_from_server_env env in
295 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
296 let open Done_or_retry in
297 match
298 ServerRename.go_ide_with_find_refs_action
300 ~find_refs_action:action
301 ~new_name
302 ~symbol_definition
303 genv
305 with
306 | Error e -> (env, Done (Error e))
307 | Ok r -> map_env r ~f:(fun x -> Ok x))
308 | REMOVE_DEAD_FIXMES codes ->
309 if genv.ServerEnv.options |> ServerArgs.no_load then (
310 log_check_response env;
311 (env, `Ok (ServerRename.get_fixme_patches codes env))
312 ) else
313 (env, `Error (remove_dead_warning "fixme"))
314 | REMOVE_DEAD_UNSAFE_CASTS ->
315 if genv.ServerEnv.options |> ServerArgs.no_load then (
316 log_check_response env;
317 (env, `Ok (ServerRename.get_dead_unsafe_cast_patches env))
318 ) else
319 (env, `Error (remove_dead_warning "unsafe cast"))
320 | REWRITE_LAMBDA_PARAMETERS files ->
321 let ctx = Provider_utils.ctx_from_server_env env in
322 (env, ServerRename.get_lambda_parameter_rewrite_patches ctx files)
323 | DUMP_SYMBOL_INFO file_list ->
324 (env, SymbolInfoService.go genv.workers file_list env)
325 | IN_MEMORY_DEP_TABLE_SIZE ->
326 (* TODO(hverr): Clean up 32-bit/migrate *)
327 (env, Ok 0)
328 | SAVE_NAMING filename ->
329 (env, SaveStateService.go_naming env.naming_table filename)
330 | SAVE_STATE (filename, gen_saved_ignore_type_errors) ->
331 if Errors.is_empty env.errorl || gen_saved_ignore_type_errors then
332 (env, SaveStateService.go env filename)
333 else
334 (env, Error "There are typecheck errors; cannot generate saved state.")
335 | CHECK_LIVENESS ->
336 (* This is for the client to know "is the server available to process requests?" *)
337 (env, ())
338 | LINT fnl ->
339 let ctx = Provider_utils.ctx_from_server_env env in
340 (env, ServerLint.go genv ctx fnl)
341 | LINT_STDIN { filename; contents } ->
342 let ctx = Provider_utils.ctx_from_server_env env in
343 (env, ServerLint.go_stdin ctx ~filename ~contents)
344 | LINT_ALL code ->
345 let ctx = Provider_utils.ctx_from_server_env env in
346 (env, ServerLint.lint_all genv ctx code)
347 | STATS -> (env, Stats.get_stats ())
348 | FORMAT (content, from, to_) ->
349 let legacy_format_options =
350 { Lsp.DocumentFormatting.tabSize = 2; insertSpaces = true }
352 (env, ServerFormat.go ~content from to_ legacy_format_options)
353 | DUMP_FULL_FIDELITY_PARSE file -> (env, FullFidelityParseService.go file)
354 | RAGE -> (env, ServerRage.go genv env)
355 | CST_SEARCH { sort_results; input; files_to_search } -> begin
357 (env, CstSearchService.go genv env ~sort_results ~files_to_search input)
358 with
359 | MultiThreadedCall.Coalesced_failures failures ->
360 let failures =
361 failures
362 |> List.map ~f:WorkerController.failure_to_string
363 |> String.concat ~sep:"\n"
365 ( env,
366 Error
367 (Printf.sprintf
368 "Worker failures - check the logs for more details:\n%s\n"
369 failures) )
370 | exn ->
371 let e = Exception.wrap exn in
372 (env, Error (Exception.to_string e))
374 | NO_PRECHECKED_FILES -> (ServerPrecheckedFiles.expand_all env, ())
375 | FUN_DEPS_BATCH positions ->
376 (env, ServerFunDepsBatch.go genv.workers positions env)
377 | LIST_FILES_WITH_ERRORS -> (env, ServerEnv.list_files env)
378 | FILE_DEPENDENTS filenames ->
379 let files = ServerFileDependents.go genv env filenames in
380 (env, files)
381 | IDENTIFY_TYPES (labelled_file, line, column) ->
382 let (path, file_input) =
383 ServerCommandTypesUtils.extract_labelled_file labelled_file
385 let (ctx, entry) = single_ctx env path file_input in
386 let result =
387 ServerTypeDefinition.go_quarantined ~ctx ~entry ~line ~column
389 (env, result)
390 | VERBOSE verbose ->
391 if verbose then
392 Hh_logger.Level.set_min_level Hh_logger.Level.Debug
393 else
394 Hh_logger.Level.set_min_level
395 genv.local_config.ServerLocalConfig.min_log_level;
396 (env, ())
397 | DEPS_OUT_BATCH positions ->
398 let ctx = Provider_utils.ctx_from_server_env env in
399 (env, ServerDepsOutBatch.go ctx positions)
400 | DEPS_IN_BATCH positions ->
401 let ctx = Provider_utils.ctx_from_server_env env in
402 (env, ServerDepsInBatch.go ~ctx ~genv ~env positions)