add `hh --client-logname`
[hiphop-php.git] / hphp / hack / src / client / clientArgs.ml
blob5c9284fd8e8e1eb139342ebb38b9a2b0907226c2
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 ClientCommand
12 open ClientEnv
14 (** Arg specs shared across more than 1 arg parser. *)
15 module Common_argspecs = struct
16 let config value_ref =
17 ( "--config",
18 Arg.String
19 (fun s -> value_ref := String_utils.split2_exn '=' s :: !value_ref),
20 " override arbitrary value from hh.conf and .hhconfig (format: <key>=<value>)"
23 let force_dormant_start value_ref =
24 ( "--force-dormant-start",
25 Arg.Bool (fun x -> value_ref := x),
26 " If server is dormant, force start a new one instead of waiting for"
27 ^ " the next one to start up automatically (default: false)" )
29 let from value_ref =
30 ( "--from",
31 Arg.Set_string value_ref,
32 " so we know who's calling hh_client - e.g. nuclide, vim, emacs, vscode"
35 let no_prechecked value_ref =
36 ( "--no-prechecked",
37 Arg.Unit (fun () -> value_ref := Some false),
38 " override value of \"prechecked_files\" flag from hh.conf" )
40 let prechecked value_ref =
41 ( "--prechecked",
42 Arg.Unit (fun () -> value_ref := Some true),
43 " override value of \"prechecked_files\" flag from hh.conf" )
45 let watchman_debug_logging value_ref =
46 ( "--watchman-debug-logging",
47 Arg.Set value_ref,
48 " Enable debug logging on Watchman client. This is very noisy" )
50 let allow_non_opt_build value_ref =
51 ( "--allow-non-opt-build",
52 Arg.Set value_ref,
53 " Override build mode check triggered by warn_on_non_opt_build .hhconfig option"
55 end
57 let parse_command () =
58 if Array.length Sys.argv < 2 then
59 CKNone
60 else
61 match String.lowercase Sys.argv.(1) with
62 | "check" -> CKCheck
63 | "start" -> CKStart
64 | "stop" -> CKStop
65 | "restart" -> CKRestart
66 | "lsp" -> CKLsp
67 | "debug" -> CKDebug
68 | "download-saved-state" -> CKDownloadSavedState
69 | "rage" -> CKRage
70 | _ -> CKNone
72 let parse_without_command options usage command =
73 let args = ref [] in
74 Arg.parse (Arg.align options) (fun x -> args := x :: !args) usage;
75 match List.rev !args with
76 | x :: rest when String.(lowercase x = lowercase command) -> rest
77 | args -> args
79 (* *** *** NB *** *** ***
80 * Commonly-used options are documented in hphp/hack/man/hh_client.1 --
81 * if you are making significant changes you need to update the manpage as
82 * well. Experimental or otherwise volatile options need not be documented
83 * there, but keep what's there up to date please. *)
84 let parse_check_args cmd =
85 (* arg parse output refs *)
86 let ai_mode = ref None in
87 let autostart = ref true in
88 let config = ref [] in
89 let dynamic_view = ref false in
90 let error_format = ref Errors.Context in
91 let force_dormant_start = ref false in
92 let format_from = ref 0 in
93 let from = ref "" in
94 let hot_classes_threshold = ref 0 in
95 let gen_saved_ignore_type_errors = ref false in
96 let ignore_hh_version = ref false in
97 let saved_state_ignore_hhconfig = ref false in
98 let log_inference_constraints = ref false in
99 let max_errors = ref None in
100 let mode = ref None in
101 let logname = ref false in
102 let monitor_logname = ref false in
103 let client_logname = ref false in
104 let ide_logname = ref false in
105 let lsp_logname = ref false in
106 let no_load = ref false in
107 let output_json = ref false in
108 let prechecked = ref None in
109 let profile_log = ref false in
110 let refactor_before = ref "" in
111 let refactor_mode = ref "" in
112 let remote = ref false in
113 let replace_state_after_saving = ref false in
114 let sort_results = ref false in
115 let timeout = ref None in
116 let version = ref false in
117 let watchman_debug_logging = ref false in
118 let allow_non_opt_build = ref false in
119 (* custom behaviors *)
120 let set_from x () = from := x in
121 let set_mode x () =
122 if Option.is_some !mode then
123 raise (Arg.Bad "only a single mode should be specified")
124 else
125 mode := Some x
127 (* parse args *)
128 let usage =
129 match cmd with
130 | CKCheck ->
131 Printf.sprintf
132 "Usage: %s check [OPTION]... [WWW-ROOT]\n\nWWW-ROOT is assumed to be current directory if unspecified\n"
133 Sys.argv.(0)
134 | CKNone ->
135 Printf.sprintf
136 ( "Usage: %s [COMMAND] [OPTION]... [WWW-ROOT]\n\nValid values for COMMAND:\n"
137 ^^ "\tcheck\t\tShows current Hack errors\n"
138 ^^ "\tstart\t\tStarts a Hack server\n"
139 ^^ "\tstop\t\tStops a Hack server\n"
140 ^^ "\trestart\t\tRestarts a Hack server\n"
141 ^^ "\tlsp\t\tRuns a persistent language service\n"
142 ^^ "\tdebug\t\tDebug mode\n"
143 ^^ "\trage\t\tReport a bug\n"
144 ^^ "\nDefault values if unspecified:\n"
145 ^^ "\tCOMMAND\t\tcheck\n"
146 ^^ "\tWWW-ROOT\tCurrent directory\n\nCheck command options:\n" )
147 Sys.argv.(0)
148 | _ -> failwith "No other keywords should make it here"
150 let options =
152 (* Please keep these sorted in the alphabetical order *)
153 ( "--ai",
154 Arg.String
155 (fun s ->
156 ai_mode :=
157 Some
158 ( ignore (Ai_options.prepare ~server:true s);
159 s )),
160 " run AI module with provided options" );
161 ( "--ai-query",
162 Arg.String (fun x -> set_mode (MODE_AI_QUERY x) ()),
163 (* Send an AI query *) "" );
164 Common_argspecs.allow_non_opt_build allow_non_opt_build;
165 ( "--auto-complete",
166 Arg.Unit (set_mode MODE_AUTO_COMPLETE),
167 " (mode) auto-completes the text on stdin" );
168 ( "--autostart-server",
169 Arg.Bool (fun x -> autostart := x),
170 " automatically start hh_server if it's not running (default: true)" );
171 ( "--bigcode",
172 Arg.String (fun filename -> set_mode (MODE_BIGCODE filename) ()),
173 " (mode) source code indexing functionalities for Big Code analysis" );
174 ( "--color",
175 Arg.String (fun x -> set_mode (MODE_COLORING x) ()),
176 " (mode) pretty prints the file content showing what is checked (give '-' for stdin)"
178 ("--colour", Arg.String (fun x -> set_mode (MODE_COLORING x) ()), " ");
179 Common_argspecs.config config;
180 ( "--coverage",
181 Arg.String (fun x -> set_mode (MODE_COVERAGE x) ()),
182 " (mode) calculates the extent of typing of a given file or directory"
184 ( "--create-checkpoint",
185 Arg.String (fun x -> set_mode (MODE_CREATE_CHECKPOINT x) ()),
186 (* Create a checkpoint which can be used to retrieve changed files later *)
187 "" );
188 ( "--cst-search",
189 Arg.Unit (set_mode (MODE_CST_SEARCH None)),
190 " (mode) Search the concrete syntax trees of files in the codebase"
191 ^ " for a given pattern" );
192 ( "--cst-search-files",
193 Arg.Rest
194 begin
195 fun fn ->
196 mode :=
197 match !mode with
198 | None
199 | Some (MODE_CST_SEARCH None) ->
200 Some (MODE_CST_SEARCH (Some [fn]))
201 | Some (MODE_CST_SEARCH (Some fnl)) ->
202 Some (MODE_CST_SEARCH (Some (fn :: fnl)))
203 | _ -> raise (Arg.Bad "only a single mode should be specified")
204 end,
205 " Run CST search on this set of files,"
206 ^ " rather than all the files in the codebase." );
207 (* Delete an existing checkpoint.
208 * Exitcode will be non-zero if no checkpoint is found *)
209 ( "--delete-checkpoint",
210 Arg.String (fun x -> set_mode (MODE_DELETE_CHECKPOINT x) ()),
211 "" );
212 ( "--dump-full-fidelity-parse",
213 Arg.String (fun x -> set_mode (MODE_FULL_FIDELITY_PARSE x) ()),
214 "" );
215 ( "--dump-symbol-info",
216 Arg.String (fun files -> set_mode (MODE_DUMP_SYMBOL_INFO files) ()),
217 (* Input format:
218 * The file list can either be "-" which accepts the input from stdin
219 * separated by newline(for long list) or directly from command line
220 * separated by semicolon.
221 * Output format:
223 * "function_calls": list of fun_calls;
225 * Note: results list can be in any order *)
226 "" );
227 ( "--dynamic-view",
228 Arg.Set dynamic_view,
229 " Replace occurrences of untyped code with dynamic" );
230 ( "--error-format",
231 Arg.String
232 (fun s ->
233 match s with
234 | "raw" -> error_format := Errors.Raw
235 | "context" -> error_format := Errors.Context
236 | _ -> print_string "Warning: unrecognized error format.\n"),
237 "<raw|context> Error formatting style" );
238 ( "--extract-standalone",
239 Arg.String (fun name -> set_mode (MODE_EXTRACT_STANDALONE name) ()),
240 " extract a given function / method together with its dependencies as a standalone file"
242 ( "--concatenate-all",
243 Arg.Unit (fun () -> set_mode MODE_CONCATENATE_ALL ()),
244 "(mode) create a single file containing all Hack code in the specified prefix"
246 ( "--file-dependents",
247 Arg.Unit
248 (fun () ->
249 let () = prechecked := Some false in
250 set_mode MODE_FILE_DEPENDENTS ()),
251 " (mode) Given a list of filepaths, shows list of (possibly) dependent files"
253 ( "--find-class-refs",
254 Arg.String (fun x -> set_mode (MODE_FIND_CLASS_REFS x) ()),
255 " (mode) finds references of the provided class name" );
256 ( "--find-refs",
257 Arg.String (fun x -> set_mode (MODE_FIND_REFS x) ()),
258 " (mode) finds references of the provided method name" );
259 Common_argspecs.force_dormant_start force_dormant_start;
260 ( "--format",
261 Arg.Tuple
263 Arg.Int (fun x -> format_from := x);
264 Arg.Int (fun x -> set_mode (MODE_FORMAT (!format_from, x)) ());
266 "" );
267 Common_argspecs.from from;
268 ( "--from-arc-diff",
269 Arg.Unit (set_from "arc_diff"),
270 " (deprecated) equivalent to --from arc_diff" );
271 ( "--from-arc-land",
272 Arg.Unit (set_from "arc_land"),
273 " (deprecated) equivalent to --from arc_land" );
274 ( "--from-check-trunk",
275 Arg.Unit (set_from "check_trunk"),
276 " (deprecated) equivalent to --from check_trunk" );
277 ( "--from-emacs",
278 Arg.Unit (set_from "emacs"),
279 " (deprecated) equivalent to --from emacs" );
280 ( "--from-vim",
281 Arg.Unit (fun () -> from := "vim"),
282 " (deprecated) equivalent to --from vim" );
283 ( "--full-fidelity-schema",
284 Arg.Unit (set_mode MODE_FULL_FIDELITY_SCHEMA),
285 "" );
286 ( "--fun-deps-at-pos-batch",
287 Arg.Rest
288 begin
289 fun position ->
290 mode :=
291 match !mode with
292 | None -> Some (MODE_FUN_DEPS_AT_POS_BATCH [position])
293 | Some (MODE_FUN_DEPS_AT_POS_BATCH positions) ->
294 Some (MODE_FUN_DEPS_AT_POS_BATCH (position :: positions))
295 | _ -> raise (Arg.Bad "only a single mode should be specified")
296 end,
297 " (mode) for each entry in input list get list of function dependencies [file:line:character list]"
299 ( "--fun-is-locallable-at-pos-batch",
300 Arg.Rest
301 begin
302 fun position ->
303 mode :=
304 match !mode with
305 | None -> Some (MODE_FUN_IS_LOCALLABLE_AT_POS_BATCH [position])
306 | Some (MODE_FUN_IS_LOCALLABLE_AT_POS_BATCH positions) ->
307 Some
308 (MODE_FUN_IS_LOCALLABLE_AT_POS_BATCH (position :: positions))
309 | _ -> raise (Arg.Bad "only a single mode should be specified")
310 end,
311 " (mode) for each entry in input list checks if function at position can be made RxLocal [file:line:character list]"
313 ( "--gen-hot-classes-file",
314 Arg.Tuple
316 Arg.Int (fun x -> hot_classes_threshold := x);
317 Arg.String
318 (fun x ->
319 set_mode (MODE_GEN_HOT_CLASSES (!hot_classes_threshold, x)) ());
321 " generate a JSON file listing all classes with more dependents than the"
322 ^ " given threshold. Usage: --gen-hot-classes-file 500 ~/hh_hot_classes.json"
324 ( "--gen-saved-ignore-type-errors",
325 Arg.Set gen_saved_ignore_type_errors,
326 " generate a saved state even if there are type errors (default: false)."
328 ( "--get-method-name",
329 Arg.String (fun x -> set_mode (MODE_IDENTIFY_SYMBOL3 x) ()),
330 (* alias for --identify-function *) "" );
331 ( "--go-to-impl-class",
332 Arg.String (fun x -> set_mode (MODE_GO_TO_IMPL_CLASS x) ()),
333 " (mode) goes to implementation of the provided class/trait/interface/etc. with the given name"
335 ( "--go-to-impl-class-remote",
336 Arg.String (fun x -> set_mode (MODE_GO_TO_IMPL_CLASS_REMOTE x) ()),
337 " (mode) similar to go-to-class-impl, but uses a glean database for faster but potentially out-of-date results"
339 ( "--go-to-impl-method",
340 Arg.String (fun x -> set_mode (MODE_GO_TO_IMPL_METHOD x) ()),
341 " (mode) goes to implementation of the provided method name" );
342 ( "--ide-find-refs",
343 Arg.String (fun x -> set_mode (MODE_IDE_FIND_REFS x) ()),
344 "" );
345 ( "--ide-get-definition",
346 Arg.String (fun x -> set_mode (MODE_IDENTIFY_SYMBOL2 x) ()),
347 (* alias for --identify-function *) "" );
348 ( "--ide-highlight-refs",
349 Arg.String (fun x -> set_mode (MODE_IDE_HIGHLIGHT_REFS x) ()),
350 (* Similar to --ide-find-refs, but returns references in current file only,
351 * and is optimized to be faster in that case *)
352 "" );
353 ( "--global-inference",
354 Arg.Rest
355 begin
356 fun fn ->
357 mode :=
358 match !mode with
359 | None ->
360 let submode =
362 - "merge" will gather all artifacts generated by typechecking with
363 global inference on and generate the global constraint graph
364 - "solve" will solve the global constraint graph and bind all
365 global type variable to a concrete type
366 - "export-json" will export the global constraint graph in a
367 json file *)
368 match fn with
369 | "merge" -> ServerGlobalInferenceTypes.MMerge
370 | "solve" -> ServerGlobalInferenceTypes.MSolve
371 | "export-json" -> ServerGlobalInferenceTypes.MExport
372 | "rewrite" -> ServerGlobalInferenceTypes.MRewrite
373 | _ ->
374 raise
375 (Arg.Bad
376 ("No " ^ fn ^ " submode supported for global inference"))
378 Some (MODE_GLOBAL_INFERENCE (submode, []))
379 | Some (MODE_GLOBAL_INFERENCE (submode, fnl)) ->
380 Some (MODE_GLOBAL_INFERENCE (submode, fn :: fnl))
381 | _ -> raise (Arg.Bad "only a single mode should be specified")
382 end,
383 " (mode) global inference operations, Usage: --global-inference "
384 ^ "[\"merge\", \"solve\", \"export-json\", \"rewrite\"] files..." );
385 ("--ide-outline", Arg.Unit (set_mode MODE_OUTLINE2), "");
386 ( "--ide-refactor",
387 Arg.String (fun x -> set_mode (MODE_IDE_REFACTOR x) ()),
388 " (mode) rename a symbol, Usage: --ide-refactor "
389 ^ " <filename>:<line number>:<col number>:<new name>" );
390 ( "--identify-function",
391 Arg.String (fun x -> set_mode (MODE_IDENTIFY_SYMBOL1 x) ()),
392 " (mode) print the full function name at the position "
393 ^ "[line:character] of the text on stdin" );
394 ( "--identify",
395 Arg.String (fun x -> set_mode (MODE_IDENTIFY_SYMBOL x) ()),
396 " (mode) identify the named symbol" );
397 ( "--ignore-hh-version",
398 Arg.Set ignore_hh_version,
399 " ignore hh_version check when loading saved states (default: false)" );
400 ( "--in-memory-dep-table-size",
401 Arg.Unit (set_mode MODE_IN_MEMORY_DEP_TABLE_SIZE),
402 " number of entries in the in-memory dependency table" );
403 ( "--inheritance-ancestor-classes",
404 Arg.String
405 (fun x -> set_mode (MODE_METHOD_JUMP_ANCESTORS (x, "Class")) ()),
406 " (mode) prints a list of classes that this class extends" );
407 ( "--inheritance-ancestor-classes-batch",
408 Arg.Rest
409 begin
410 fun class_ ->
411 mode :=
412 match !mode with
413 | None ->
414 Some (MODE_METHOD_JUMP_ANCESTORS_BATCH ([class_], "Class"))
415 | Some (MODE_METHOD_JUMP_ANCESTORS_BATCH (classes, "Class")) ->
416 Some
417 (MODE_METHOD_JUMP_ANCESTORS_BATCH (class_ :: classes, "Class"))
418 | _ -> raise (Arg.Bad "only a single mode should be specified")
419 end,
420 " (mode) prints a list of classes that these classes extend" );
421 ( "--inheritance-ancestor-interfaces",
422 Arg.String
423 (fun x -> set_mode (MODE_METHOD_JUMP_ANCESTORS (x, "Interface")) ()),
424 " (mode) prints a list of interfaces that this class implements" );
425 ( "--inheritance-ancestor-interfaces-batch",
426 Arg.Rest
427 begin
428 fun class_ ->
429 mode :=
430 match !mode with
431 | None ->
432 Some (MODE_METHOD_JUMP_ANCESTORS_BATCH ([class_], "Interface"))
433 | Some (MODE_METHOD_JUMP_ANCESTORS_BATCH (classes, "Interface"))
435 Some
436 (MODE_METHOD_JUMP_ANCESTORS_BATCH
437 (class_ :: classes, "Interface"))
438 | _ -> raise (Arg.Bad "only a single mode should be specified")
439 end,
440 " (mode) prints a list of interfaces that these classes implement" );
441 ( "--inheritance-ancestor-traits",
442 Arg.String
443 (fun x -> set_mode (MODE_METHOD_JUMP_ANCESTORS (x, "Trait")) ()),
444 " (mode) prints a list of traits that this class uses" );
445 ( "--inheritance-ancestor-traits-batch",
446 Arg.Rest
447 begin
448 fun class_ ->
449 mode :=
450 match !mode with
451 | None ->
452 Some (MODE_METHOD_JUMP_ANCESTORS_BATCH ([class_], "Trait"))
453 | Some (MODE_METHOD_JUMP_ANCESTORS_BATCH (classes, "Trait")) ->
454 Some
455 (MODE_METHOD_JUMP_ANCESTORS_BATCH (class_ :: classes, "Trait"))
456 | _ -> raise (Arg.Bad "only a single mode should be specified")
457 end,
458 " (mode) prints a list of traits that these classes use" );
459 ( "--inheritance-ancestors",
460 Arg.String
461 (fun x -> set_mode (MODE_METHOD_JUMP_ANCESTORS (x, "No_filter")) ()),
462 " (mode) prints a list of all related classes or methods"
463 ^ " to the given class" );
464 ( "--inheritance-children",
465 Arg.String (fun x -> set_mode (MODE_METHOD_JUMP_CHILDREN x) ()),
466 " (mode) prints a list of all related classes or methods"
467 ^ " to the given class" );
468 ( "--json",
469 Arg.Set output_json,
470 " output json for machine consumption. (default: false)" );
471 ( "--lint",
472 Arg.Unit (set_mode MODE_LINT),
473 " (mode) lint the given list of files" );
474 ( "--lint-all",
475 Arg.Int (fun x -> set_mode (MODE_LINT_ALL x) ()),
476 " (mode) find all occurrences of lint with the given error code" );
477 ( "--lint-stdin",
478 Arg.String (fun filename -> set_mode (MODE_LINT_STDIN filename) ()),
479 " (mode) lint a file given on stdin; the filename should be the"
480 ^ " argument to this option" );
481 ( "--lint-xcontroller",
482 Arg.String
483 (fun filename -> set_mode (MODE_LINT_XCONTROLLER filename) ()),
484 "" )
485 (* (mode) lint all xcontrollers in files listed in given file (i.e. the argument is
486 * a path to a file that contains a list of files) *);
487 ( "--list-files",
488 Arg.Unit (set_mode MODE_LIST_FILES),
489 " (mode) list files with errors" );
490 ( "--log-inference-constraints",
491 Arg.Set log_inference_constraints,
492 " (for hh debugging purpose only) log type"
493 ^ " inference constraints into external logger (e.g. Scuba)" );
494 ( "--max-errors",
495 Arg.Int (fun num_errors -> max_errors := Some num_errors),
496 " Maximum number of errors to display" );
497 ("--logname", Arg.Set logname, " (mode) show log filename and exit");
498 ( "--monitor-logname",
499 Arg.Set monitor_logname,
500 " (mode) show monitor log filename and exit" );
501 ( "--client-logname",
502 Arg.Set client_logname,
503 " (mode) show client log filename and exit" );
504 ( "--ide-logname",
505 Arg.Set ide_logname,
506 " (mode) show client ide log filename and exit" );
507 ( "--lsp-logname",
508 Arg.Set lsp_logname,
509 " (mode) show client lsp log filename and exit" );
510 ("--no-load", Arg.Set no_load, " start from a fresh state");
511 ( "--outline",
512 Arg.Unit (set_mode MODE_OUTLINE),
513 " (mode) prints an outline of the text on stdin" );
514 Common_argspecs.prechecked prechecked;
515 Common_argspecs.no_prechecked prechecked;
516 ( "--pause",
517 Arg.Unit (set_mode (MODE_PAUSE true)),
518 " (mode) pause recheck-on-file-change [EXPERIMENTAL]" );
519 ("--profile-log", Arg.Set profile_log, " enable profile logging");
520 ( "--refactor",
521 Arg.Tuple
523 Arg.Symbol
524 (["Class"; "Function"; "Method"], (fun x -> refactor_mode := x));
525 Arg.String (fun x -> refactor_before := x);
526 Arg.String
527 (fun x ->
528 set_mode
529 (MODE_REFACTOR (!refactor_mode, !refactor_before, x))
530 ());
532 " (mode) rename a symbol, Usage: --refactor "
533 ^ "[\"Class\", \"Function\", \"Method\"] <Current Name> <New Name>" );
534 ("--remote", Arg.Set remote, " force remote type checking");
535 ( "--remove-dead-fixme",
536 Arg.Int
537 begin
538 fun code ->
539 mode :=
540 match !mode with
541 | None -> Some (MODE_REMOVE_DEAD_FIXMES [code])
542 | Some (MODE_REMOVE_DEAD_FIXMES codel) ->
543 Some (MODE_REMOVE_DEAD_FIXMES (code :: codel))
544 | _ -> raise (Arg.Bad "only a single mode should be specified")
545 end,
546 " (mode) remove dead HH_FIXME for specified error code "
547 ^ "(first do hh_client restart --no-load)" );
548 ( "--remove-dead-fixmes",
549 Arg.Unit (set_mode (MODE_REMOVE_DEAD_FIXMES [])),
550 " (mode) remove dead HH_FIXME for any error code < 5000 "
551 ^ "(first do hh_client restart --no-load)" );
552 ( "--replace-state-after-saving",
553 Arg.Set replace_state_after_saving,
554 " if combined with --save-mini, causes the saved state"
555 ^ " to replace the program state; otherwise, the state files are not"
556 ^ " used after being written to disk (default: false)" );
557 ( "--resume",
558 Arg.Unit (set_mode (MODE_PAUSE false)),
559 " (mode) resume recheck-on-file-change [EXPERIMENTAL]" );
560 ( "--retries",
561 Arg.Int (fun n -> timeout := Some (float_of_int (max 5 n))),
562 " (deprecated) same as --timeout" );
563 (* Retrieve changed files since input checkpoint.
564 * Output is separated by newline.
565 * Exit code will be non-zero if no checkpoint is found *)
566 ( "--retrieve-checkpoint",
567 Arg.String (fun x -> set_mode (MODE_RETRIEVE_CHECKPOINT x) ()),
568 "" );
569 ("--retry-if-init", Arg.Bool (fun _ -> ()), " (deprecated and ignored)");
570 ( "--rewrite-lambda-parameters",
571 Arg.Rest
572 begin
573 fun fn ->
574 mode :=
575 match !mode with
576 | None -> Some (MODE_REWRITE_LAMBDA_PARAMETERS [fn])
577 | Some (MODE_REWRITE_LAMBDA_PARAMETERS fnl) ->
578 Some (MODE_REWRITE_LAMBDA_PARAMETERS (fn :: fnl))
579 | _ -> raise (Arg.Bad "only a single mode should be specified")
580 end,
581 " (mode) rewrite lambdas in the files from the given list"
582 ^ " with suggested parameter types" );
583 ( "--rewrite-partial-parameters-type-hints",
584 Arg.Rest
585 begin
586 fun fn ->
587 mode :=
588 match !mode with
589 | None -> Some (MODE_REWRITE_TYPE_PARAMS_TYPE [fn])
590 | Some (MODE_REWRITE_TYPE_PARAMS_TYPE fnl) ->
591 Some (MODE_REWRITE_TYPE_PARAMS_TYPE (fn :: fnl))
592 | _ -> raise (Arg.Bad "only a single mode should be specified")
593 end,
594 " (mode) add missing type parameters in the type hints for function"
595 ^ " parameters (e.g.: C $x -> C<int> $x) in the files from the given list"
597 ( "--save-naming",
598 Arg.String (fun x -> set_mode (MODE_SAVE_NAMING x) ()),
599 " (mode) Save the naming table to the given file."
600 ^ " Returns the number of files and symbols written to disk." );
601 ( "--save-state",
602 Arg.String (fun x -> set_mode (MODE_SAVE_STATE x) ()),
603 " (mode) Save a saved state to the given file."
604 ^ " Returns number of edges dumped from memory to the database." );
605 ( "--saved-state-ignore-hhconfig",
606 Arg.Set saved_state_ignore_hhconfig,
607 " ignore hhconfig hash when loading saved states (default: false)" );
608 ( "--search",
609 Arg.String (fun x -> set_mode (MODE_SEARCH (x, "")) ()),
610 " (mode) fuzzy search symbol definitions" );
611 ( "--search-class",
612 Arg.String (fun x -> set_mode (MODE_SEARCH (x, "class")) ()),
613 " (mode) fuzzy search class definitions" );
614 ( "--search-constant",
615 Arg.String (fun x -> set_mode (MODE_SEARCH (x, "constant")) ()),
616 " (mode) fuzzy search constant definitions" );
617 ( "--search-function",
618 Arg.String (fun x -> set_mode (MODE_SEARCH (x, "function")) ()),
619 " (mode) fuzzy search function definitions" );
620 ( "--search-typedef",
621 Arg.String (fun x -> set_mode (MODE_SEARCH (x, "typedef")) ()),
622 " (mode) fuzzy search typedef definitions" );
623 ( "--server-rage",
624 Arg.Unit (set_mode MODE_SERVER_RAGE),
625 " (mode) dumps internal state of hh_server" );
626 ( "--single",
627 Arg.String (fun x -> set_mode (MODE_STATUS_SINGLE x) ()),
628 "<path> Return errors in file with provided name (give '-' for stdin)"
630 ("--sort-results", Arg.Set sort_results, " sort output for CST search.");
631 ( "--stats",
632 Arg.Unit (set_mode MODE_STATS),
633 " display some server statistics" );
634 ( "--status",
635 Arg.Unit (set_mode MODE_STATUS),
636 " (mode) show a human readable list of errors (default)" );
637 ( "--timeout",
638 Arg.Float (fun x -> timeout := Some (Float.max 5. x)),
639 " set the timeout in seconds (default: no timeout)" );
640 ( "--type-at-pos",
641 Arg.String (fun x -> set_mode (MODE_TYPE_AT_POS x) ()),
642 " (mode) show type at a given position in file [line:character]" );
643 ( "--type-at-pos-batch",
644 Arg.Rest
645 begin
646 fun position ->
647 mode :=
648 match !mode with
649 | None -> Some (MODE_TYPE_AT_POS_BATCH [position])
650 | Some (MODE_TYPE_AT_POS_BATCH positions) ->
651 Some (MODE_TYPE_AT_POS_BATCH (position :: positions))
652 | _ -> raise (Arg.Bad "only a single mode should be specified")
653 end,
654 " (mode) show types at multiple positions [file:line:character list]" );
655 ( "--verbose-on",
656 Arg.Unit (fun () -> set_mode (MODE_VERBOSE true) ()),
657 " (mode) turn on verbose server log" );
658 ( "--verbose-off",
659 Arg.Unit (fun () -> set_mode (MODE_VERBOSE false) ()),
660 " (mode) turn off verbose server log" );
661 ("--version", Arg.Set version, " (mode) show version and exit");
662 Common_argspecs.watchman_debug_logging watchman_debug_logging;
663 (* Please keep these sorted in the alphabetical order *)
666 let args = parse_without_command options usage "check" in
667 if !version then (
668 if !output_json then
669 ServerArgs.print_json_version ()
670 else
671 print_endline Hh_version.version;
672 exit 0
675 let mode = Option.value !mode ~default:MODE_STATUS in
676 (* fixups *)
677 let (root, paths) =
678 match (mode, args) with
679 | (MODE_LINT, _)
680 | (MODE_CONCATENATE_ALL, _)
681 | (MODE_FILE_DEPENDENTS, _) ->
682 (Wwwroot.get None, args)
683 | (_, []) -> (Wwwroot.get None, [])
684 | (_, [x]) -> (Wwwroot.get (Some x), [])
685 | (_, _) ->
686 Printf.fprintf
687 stderr
688 "Error: please provide at most one www directory\n%!";
689 exit 1
691 if !ide_logname then (
692 let ide_log_link = ServerFiles.client_ide_log root in
693 Printf.printf "%s\n%!" ide_log_link;
694 exit 0
697 if !lsp_logname then (
698 let lsp_log_link = ServerFiles.client_lsp_log root in
699 Printf.printf "%s\n%!" lsp_log_link;
700 exit 0
703 if !monitor_logname then (
704 let monitor_log_link = ServerFiles.monitor_log_link root in
705 Printf.printf "%s\n%!" monitor_log_link;
706 exit 0
709 if !client_logname then (
710 let client_log_link = ServerFiles.client_log root in
711 Printf.printf "%s\n%!" client_log_link;
712 exit 0
715 if !logname then (
716 let log_link = ServerFiles.log_link root in
717 Printf.printf "%s\n%!" log_link;
718 exit 0
721 let () =
722 if String.equal !from "emacs" then
723 Printf.fprintf stdout "-*- mode: compilation -*-\n%!"
725 CCheck
727 ai_mode = !ai_mode;
728 autostart = !autostart;
729 config = !config;
730 dynamic_view = !dynamic_view;
731 error_format = !error_format;
732 force_dormant_start = !force_dormant_start;
733 from = !from;
734 gen_saved_ignore_type_errors = !gen_saved_ignore_type_errors;
735 ignore_hh_version = !ignore_hh_version;
736 saved_state_ignore_hhconfig = !saved_state_ignore_hhconfig;
737 paths;
738 log_inference_constraints = !log_inference_constraints;
739 max_errors = !max_errors;
740 mode;
741 no_load =
742 ( !no_load
744 match mode with
745 | MODE_REMOVE_DEAD_FIXMES _ -> true
746 | _ -> false );
747 output_json = !output_json;
748 prechecked = !prechecked;
749 profile_log = !profile_log;
750 remote = !remote;
751 replace_state_after_saving = !replace_state_after_saving;
752 root;
753 sort_results = !sort_results;
754 deadline = Option.map ~f:(fun t -> Unix.time () +. t) !timeout;
755 watchman_debug_logging = !watchman_debug_logging;
756 allow_non_opt_build = !allow_non_opt_build;
759 let parse_start_env command =
760 let usage =
761 Printf.sprintf
762 "Usage: %s %s [OPTION]... [WWW-ROOT]\n%s a Hack server\n\nWWW-ROOT is assumed to be current directory if unspecified\n"
763 Sys.argv.(0)
764 command
765 (String.capitalize command)
767 let log_inference_constraints = ref false in
768 let no_load = ref false in
769 let watchman_debug_logging = ref false in
770 let profile_log = ref false in
771 let ai_mode = ref None in
772 let ignore_hh_version = ref false in
773 let saved_state_ignore_hhconfig = ref false in
774 let prechecked = ref None in
775 let from = ref "" in
776 let config = ref [] in
777 let allow_non_opt_build = ref false in
778 let wait_deprecation_msg () =
779 Printf.eprintf
780 "WARNING: --wait is deprecated, does nothing, and will be going away soon!\n%!"
782 let options =
784 (* Please keep these sorted in the alphabetical order *)
785 ("--ai", Arg.String (fun x -> ai_mode := Some x), " run ai with options ");
786 Common_argspecs.allow_non_opt_build allow_non_opt_build;
787 Common_argspecs.config config;
788 Common_argspecs.from from;
789 ( "--ignore-hh-version",
790 Arg.Set ignore_hh_version,
791 " ignore hh_version check when loading saved states (default: false)" );
792 ( "--log-inference-constraints",
793 Arg.Set log_inference_constraints,
794 " (for hh debugging purpose only) log type"
795 ^ " inference constraints into external logger (e.g. Scuba)" );
796 ("--no-load", Arg.Set no_load, " start from a fresh state");
797 Common_argspecs.no_prechecked prechecked;
798 Common_argspecs.prechecked prechecked;
799 ("--profile-log", Arg.Set profile_log, " enable profile logging");
800 ( "--saved-state-ignore-hhconfig",
801 Arg.Set saved_state_ignore_hhconfig,
802 " ignore hhconfig hash when loading saved states (default: false)" );
803 ( "--wait",
804 Arg.Unit wait_deprecation_msg,
805 " this flag is deprecated and does nothing!" );
806 Common_argspecs.watchman_debug_logging watchman_debug_logging;
807 (* Please keep these sorted in the alphabetical order *)
810 let args = parse_without_command options usage command in
811 let root =
812 match args with
813 | [] -> Wwwroot.get None
814 | [x] -> Wwwroot.get (Some x)
815 | _ ->
816 Printf.fprintf
817 stderr
818 "Error: please provide at most one www directory\n%!";
819 exit 1
822 ClientStart.ai_mode = !ai_mode;
823 config = !config;
824 debug_port = None;
825 dynamic_view = false;
826 exit_on_failure = true;
827 from = !from;
828 ignore_hh_version = !ignore_hh_version;
829 saved_state_ignore_hhconfig = !saved_state_ignore_hhconfig;
830 log_inference_constraints = !log_inference_constraints;
831 no_load = !no_load;
832 prechecked = !prechecked;
833 profile_log = !profile_log;
834 root;
835 silent = false;
836 watchman_debug_logging = !watchman_debug_logging;
837 allow_non_opt_build = !allow_non_opt_build;
840 let parse_start_args () = CStart (parse_start_env "start")
842 let parse_restart_args () = CRestart (parse_start_env "restart")
844 let parse_stop_args () =
845 let usage =
846 Printf.sprintf
847 "Usage: %s stop [OPTION]... [WWW-ROOT]\nStop a hack server\n\nWWW-ROOT is assumed to be current directory if unspecified\n"
848 Sys.argv.(0)
850 let from = ref "" in
851 let options = [Common_argspecs.from from] in
852 let args = parse_without_command options usage "stop" in
853 let root =
854 match args with
855 | [] -> Wwwroot.get None
856 | [x] -> Wwwroot.get (Some x)
857 | _ ->
858 Printf.fprintf
859 stderr
860 "Error: please provide at most one www directory\n%!";
861 exit 1
863 CStop { ClientStop.root; from = !from }
865 let parse_lsp_args ~(init_id : string) =
866 let usage =
867 Printf.sprintf
868 "Usage: %s lsp [OPTION]...\nRuns a persistent language service\n"
869 Sys.argv.(0)
871 let from = ref "" in
872 let config = ref [] in
873 let use_ffp_autocomplete = ref false in
874 let use_ranked_autocomplete = ref false in
875 let use_serverless_ide = ref false in
876 let verbose = ref false in
877 let options =
879 (* Please keep these sorted in the alphabetical order *)
880 ("--enhanced-hover", Arg.Unit (fun () -> ()), " [legacy] no-op");
881 ( "--ffp-autocomplete",
882 Arg.Set use_ffp_autocomplete,
883 " [experimental] use the full-fidelity parser based autocomplete " );
884 Common_argspecs.from from;
885 Common_argspecs.config config;
886 ( "--ranked-autocomplete",
887 Arg.Set use_ranked_autocomplete,
888 " [experimental] display ranked autocompletion results" );
889 ( "--serverless-ide",
890 Arg.Set use_serverless_ide,
891 " [experimental] provide IDE services from hh_client instead of hh_server"
893 ( "--verbose",
894 Arg.Set verbose,
895 " verbose logs to stderr and `hh --ide-logname` and `--lsp-logname`" );
896 (* Please keep these sorted in the alphabetical order *)
899 let args = parse_without_command options usage "lsp" in
900 match args with
901 | [] ->
902 CLsp
904 ClientLsp.from = !from;
905 config = !config;
906 use_ffp_autocomplete = !use_ffp_autocomplete;
907 use_ranked_autocomplete = !use_ranked_autocomplete;
908 use_serverless_ide = !use_serverless_ide;
909 verbose = !verbose;
910 init_id;
912 | _ ->
913 Printf.printf "%s\n" usage;
914 exit 2
916 let parse_debug_args () =
917 let usage =
918 Printf.sprintf "Usage: %s debug [OPTION]... [WWW-ROOT]\n" Sys.argv.(0)
920 let from = ref "" in
921 let options = [Common_argspecs.from from] in
922 let args = parse_without_command options usage "debug" in
923 let root =
924 match args with
925 | [] -> Wwwroot.get None
926 | [x] -> Wwwroot.get (Some x)
927 | _ ->
928 Printf.printf "%s\n" usage;
929 exit 2
931 CDebug { ClientDebug.root; from = !from }
933 let parse_rage_args () =
934 let usage =
935 Printf.sprintf "Usage: %s rage [OPTION]... [WWW-ROOT]\n" Sys.argv.(0)
937 let from = ref "" in
938 let desc = ref None in
939 let rageid = ref None in
940 let options =
942 Common_argspecs.from from;
943 ("--desc", Arg.String (fun s -> desc := Some s), " description of problem");
944 ( "--rageid",
945 Arg.String (fun s -> rageid := Some s),
946 " (optional) use this id, and finish even if parent process dies" );
949 let args = parse_without_command options usage "rage" in
950 let root =
951 match args with
952 | [] -> Wwwroot.get None
953 | [x] -> Wwwroot.get (Some x)
954 | _ ->
955 Printf.printf "%s\n" usage;
956 exit 2
958 (* hh_client normally handles Ctrl+C by printing an exception-stack.
959 But for us, in an interactive prompt, Ctrl+C is an unexceptional way to quit. *)
960 Sys_utils.set_signal Sys.sigint Sys.Signal_default;
962 let desc =
963 match !desc with
964 | Some desc -> desc
965 | None ->
966 Printf.printf
967 ( "Sorry that hh isn't working. What's wrong?\n"
968 ^^ "1. hh_server takes ages to initialize\n"
969 ^^ "2. hh is stuck in an infinite loop\n"
970 ^^ "3. hh gives some error message about the monitor\n"
971 ^^ "4. hack says it has an internal typecheck bug and asked me to report it\n"
972 ^^ "5. hack is reporting errors that are clearly incorrect [please elaborate]\n"
973 ^^ "6. I'm not sure how to write my code to avoid these hack errors\n"
974 ^^ "7. hh says something about unsaved changes from an editor even after I've quit my editor\n"
975 ^^ "8. something's wrong with hack VS Code or other editor\n"
976 ^^ "[other] Please type either one of the above numbers, or a freeform description\n"
977 ^^ "\nrage> %!" );
978 let response = In_channel.input_line_exn In_channel.stdin in
979 let (response, info) =
980 if String.equal response "1" then
981 ("hh_server slow initialize", `Verbose_hh_start)
982 else if String.equal response "2" then
983 ("hh stuck in infinite loop", `Verbose_hh_start)
984 else if String.equal response "3" then
985 ("hh monitor problem", `Verbose_hh_start)
986 else if String.equal response "4" then
987 ("internal typecheck bug", `No_info)
988 else if String.equal response "5" then
989 let () =
990 Printf.printf
991 "Please elaborate on which errors are incorrect...\nrage> %!"
993 (In_channel.input_line_exn In_channel.stdin, `No_info)
994 else if String.equal response "6" then
995 let () =
996 Printf.printf
997 ( "Please ask in the appropriate support groups for advice on coding in Hack; "
998 ^^ "`hh rage` is solely for reporting bugs in the tooling, not for reporting typechecker or "
999 ^^ "language issues." )
1001 exit 0
1002 else if String.equal response "7" then
1003 ("unsaved editor changes", `Unsaved)
1004 else if String.equal response "8" then
1005 let () =
1006 Printf.printf
1007 ( "Please file the bug from within your editor to capture the right logs. "
1008 ^^ "Note: you can do Preferences > Settings > Hack > Verbose, then `pkill hh_client`, "
1009 ^^ "then reproduce the error, then file the bug. This way we'll get even richer logs.\n%!"
1012 exit 0
1013 else
1014 (response, `Verbose_hh_start)
1016 begin
1017 match info with
1018 | `No_info -> ()
1019 | `Verbose_hh_start ->
1020 Printf.printf
1021 ( "\nPOWER USERS ONLY: Sometimes the normal logging from hh_server isn't "
1022 ^^ "enough to diagnose an issue, and we'll ask you to switch hh_server to "
1023 ^^ "write verbose logs, then have you repro the issue, then use `hh rage` to "
1024 ^^ "gather up those now-verbose logs. To restart hh_server with verbose logs, "
1025 ^^ "do `hh stop && hh start --config min_log_level=debug`. "
1026 ^^ "Once done, then you can repro the issue, and then do rage again.\n\n%!"
1028 | `Unsaved ->
1029 Printf.printf
1030 "\nNote: you can often work around this issue yourself by quitting your editor, then `pkill hh_client`.\n%!"
1031 end;
1032 response
1034 CRage { ClientRage.root; from = !from; desc; rageid = !rageid }
1036 let parse_download_saved_state_args () =
1037 let usage =
1038 Printf.sprintf
1039 {|Usage: %s download-saved-state [OPTION]... [WWW-ROOT]
1041 Download a saved-state to disk for the given repository, to make future
1042 invocations of `hh` faster.|}
1043 Sys.argv.(0)
1045 let valid_types_message =
1046 "Valid values are: naming-and-dep-table, naming-table"
1049 let from = ref "" in
1050 let saved_state_type = ref None in
1051 let options =
1052 Arg.align
1054 Common_argspecs.from from;
1055 ( "--type",
1056 Arg.String (fun arg -> saved_state_type := Some arg),
1057 Printf.sprintf
1058 "The type of saved-state to download. %s"
1059 valid_types_message );
1062 let args = parse_without_command options usage "download-saved-state" in
1063 let root =
1064 match args with
1065 | [x] -> Wwwroot.get (Some x)
1066 | _ ->
1067 print_endline usage;
1068 exit 2
1070 let from =
1071 match !from with
1072 | "" ->
1073 print_endline "The '--from' option is required.";
1074 exit 2
1075 | from -> from
1077 let saved_state_type =
1078 match !saved_state_type with
1079 | None ->
1080 Printf.printf "The '--type' option is required. %s\n" valid_types_message;
1081 exit 2
1082 | Some "naming-and-dep-table" ->
1083 ClientDownloadSavedState.Naming_and_dep_table
1084 | Some "naming-table" -> ClientDownloadSavedState.Naming_table
1085 | Some saved_state_type ->
1086 Printf.printf
1087 "Unrecognized value '%s' for '--type'. %s\n"
1088 saved_state_type
1089 valid_types_message;
1090 exit 2
1092 CDownloadSavedState { ClientDownloadSavedState.root; from; saved_state_type }
1094 let parse_args ~(init_id : string) =
1095 match parse_command () with
1096 | (CKNone | CKCheck) as cmd -> parse_check_args cmd
1097 | CKStart -> parse_start_args ()
1098 | CKStop -> parse_stop_args ()
1099 | CKRestart -> parse_restart_args ()
1100 | CKDebug -> parse_debug_args ()
1101 | CKLsp -> parse_lsp_args ~init_id
1102 | CKRage -> parse_rage_args ()
1103 | CKDownloadSavedState -> parse_download_saved_state_args ()
1105 let root = function
1106 | CCheck { ClientEnv.root; _ }
1107 | CStart { ClientStart.root; _ }
1108 | CRestart { ClientStart.root; _ }
1109 | CStop { ClientStop.root; _ }
1110 | CDebug { ClientDebug.root; _ }
1111 | CRage { ClientRage.root; _ }
1112 | CDownloadSavedState { ClientDownloadSavedState.root; _ } ->
1113 Some root
1114 | CLsp _ -> None