Skip to content

Commit 72c30a7

Browse files
grievejiafacebook-github-bot
authored andcommitted
Add new analyze command (2/4) -- cli arguments
Reviewed By: dkgi Differential Revision: D30466705 fbshipit-source-id: 62eba321a07dbdfe456d58ef65614c028be639f0
1 parent f69fdc2 commit 72c30a7

File tree

7 files changed

+259
-13
lines changed

7 files changed

+259
-13
lines changed

source/command/newAnalyzeCommand.ml

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,122 @@
66
*)
77

88
open Core
9+
module Path = PyrePath
910

1011
(* Analyze command uses the same exit code scheme as check command. *)
1112
module ExitStatus = NewCheckCommand.ExitStatus
1213

13-
let run_analyze _configuration_file =
14+
module AnalyzeConfiguration = struct
15+
type t = {
16+
base: NewCommandStartup.BaseConfiguration.t;
17+
dump_call_graph: bool;
18+
dump_model_query_results: bool;
19+
find_missing_flows: string option;
20+
inline_decorators: bool;
21+
maximum_tito_depth: int option;
22+
maximum_trace_length: int option;
23+
no_verify: bool;
24+
repository_root: Path.t option;
25+
rule_filter: int list option;
26+
save_results_to: Path.t option;
27+
strict: bool;
28+
taint_model_paths: Path.t list;
29+
use_cache: bool;
30+
}
31+
[@@deriving sexp, compare, hash]
32+
33+
let of_yojson json =
34+
let open Yojson.Safe.Util in
35+
let open JsonParsing in
36+
(* Parsing logic *)
37+
try
38+
match NewCommandStartup.BaseConfiguration.of_yojson json with
39+
| Result.Error _ as error -> error
40+
| Result.Ok base ->
41+
let dump_call_graph = bool_member "dump_call_graph" ~default:false json in
42+
let dump_model_query_results =
43+
bool_member "dump_model_query_results" ~default:false json
44+
in
45+
let find_missing_flows = optional_string_member "find_missing_flows" json in
46+
let inline_decorators = bool_member "inline_decorators" ~default:false json in
47+
let maximum_tito_depth = optional_int_member "maximum_tito_depth" json in
48+
let maximum_trace_length = optional_int_member "maximum_trace_length" json in
49+
let no_verify = bool_member "no_verify" ~default:false json in
50+
let repository_root = optional_path_member "repository_root" json in
51+
let rule_filter =
52+
member "rule_filter" json
53+
|> function
54+
| `Null -> None
55+
| _ as json -> Some (convert_each to_int json)
56+
in
57+
let save_results_to = optional_path_member "save_results_to" json in
58+
let strict = bool_member "strict" ~default:false json in
59+
let taint_model_paths = json |> path_list_member "taint_model_paths" ~default:[] in
60+
let use_cache = bool_member "use_cache" ~default:false json in
61+
62+
Result.Ok
63+
{
64+
base;
65+
dump_call_graph;
66+
dump_model_query_results;
67+
find_missing_flows;
68+
inline_decorators;
69+
maximum_tito_depth;
70+
maximum_trace_length;
71+
no_verify;
72+
repository_root;
73+
rule_filter;
74+
save_results_to;
75+
strict;
76+
taint_model_paths;
77+
use_cache;
78+
}
79+
with
80+
| Type_error (message, _)
81+
| Undefined (message, _) ->
82+
Result.Error message
83+
| other_exception -> Result.Error (Exn.to_string other_exception)
84+
end
85+
86+
let run_analyze _analyze_configuraiton =
1487
Log.warning "Coming soon...";
15-
let exit_status = ExitStatus.Ok in
88+
Lwt.return ExitStatus.Ok
89+
90+
91+
let run_analyze configuration_file =
92+
let exit_status =
93+
match
94+
NewCommandStartup.read_and_parse_json configuration_file ~f:AnalyzeConfiguration.of_yojson
95+
with
96+
| Result.Error message ->
97+
Log.error "%s" message;
98+
ExitStatus.PyreError
99+
| Result.Ok
100+
({
101+
AnalyzeConfiguration.base =
102+
{
103+
NewCommandStartup.BaseConfiguration.global_root;
104+
local_root;
105+
debug;
106+
remote_logging;
107+
profiling_output;
108+
memory_profiling_output;
109+
_;
110+
};
111+
_;
112+
} as analyze_configuration) ->
113+
NewCommandStartup.setup_global_states
114+
~global_root
115+
~local_root
116+
~debug
117+
~additional_logging_sections:[]
118+
~remote_logging
119+
~profiling_output
120+
~memory_profiling_output
121+
();
122+
Lwt_main.run
123+
(Lwt.catch (fun () -> run_analyze analyze_configuration) NewCheckCommand.on_exception)
124+
in
16125
Statistics.flush ();
17126
exit (ExitStatus.exit_code exit_status)
18127

source/command/newAnalyzeCommand.mli

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,26 @@
77

88
open Core
99

10+
module AnalyzeConfiguration : sig
11+
type t = {
12+
base: NewCommandStartup.BaseConfiguration.t;
13+
dump_call_graph: bool;
14+
dump_model_query_results: bool;
15+
find_missing_flows: string option;
16+
inline_decorators: bool;
17+
maximum_tito_depth: int option;
18+
maximum_trace_length: int option;
19+
no_verify: bool;
20+
repository_root: PyrePath.t option;
21+
rule_filter: int list option;
22+
save_results_to: PyrePath.t option;
23+
strict: bool;
24+
taint_model_paths: PyrePath.t list;
25+
use_cache: bool;
26+
}
27+
[@@deriving sexp, compare, hash]
28+
29+
val of_yojson : Yojson.Safe.t -> (t, string) Result.t
30+
end
31+
1032
val command : Command.t

source/command/test/dune

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
pyrelib.test pyrelib.ast pyrelib.analysis))
77

88
(tests
9-
(names baseConfigurationTest newCheckTest newInferTest newServerTest)
10-
(modules baseConfigurationTest newCheckTest newInferTest newServerTest)
9+
(names baseConfigurationTest newAnalyzeTest newCheckTest newInferTest
10+
newServerTest)
11+
(modules baseConfigurationTest newAnalyzeTest newCheckTest newInferTest
12+
newServerTest)
1113
(preprocess
1214
(pps ppx_compare ppx_sexp_conv))
1315
(libraries oUnit pyrelib.test pyrelib.commands))
1416

1517
(tests
1618
(names languageServerTest persistentClientTest serverProtocolTest serverTest)
17-
(modules :standard \ commandTest baseConfigurationTest newCheckTest newInferTest newServerTest)
19+
(modules :standard \ commandTest baseConfigurationTest newAnalyzeTest
20+
newCheckTest newInferTest newServerTest)
1821
(libraries oUnit pyrelib.test pyrelib.commandTest pyrelib.commands
1922
pyrelib.languageServer pyrelib.network pyrelib.server pyrelib.ast))
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
(*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*)
7+
8+
open Core
9+
open OUnit2
10+
open Commands.NewAnalyze
11+
module Path = PyrePath
12+
13+
let test_json_parsing context =
14+
let assert_parsed ~expected json =
15+
match AnalyzeConfiguration.of_yojson json with
16+
| Result.Error message ->
17+
let message = Format.sprintf "Unexpected JSON parsing failure: %s" message in
18+
assert_failure message
19+
| Result.Ok actual ->
20+
assert_equal
21+
~ctxt:context
22+
~cmp:[%compare.equal: AnalyzeConfiguration.t]
23+
~printer:(fun result -> Sexp.to_string ([%sexp_of: AnalyzeConfiguration.t] result))
24+
expected
25+
actual
26+
in
27+
28+
let dummy_analyze_configuration =
29+
{
30+
AnalyzeConfiguration.base = BaseConfigurationTest.dummy_base_configuration;
31+
dump_call_graph = false;
32+
dump_model_query_results = false;
33+
find_missing_flows = None;
34+
inline_decorators = false;
35+
maximum_tito_depth = None;
36+
maximum_trace_length = None;
37+
repository_root = None;
38+
rule_filter = None;
39+
save_results_to = None;
40+
strict = false;
41+
taint_model_paths = [];
42+
use_cache = false;
43+
no_verify = false;
44+
}
45+
in
46+
47+
assert_parsed
48+
(`Assoc (("dump_call_graph", `Bool true) :: BaseConfigurationTest.dummy_base_json))
49+
~expected:{ dummy_analyze_configuration with dump_call_graph = true };
50+
assert_parsed
51+
(`Assoc (("dump_model_query_results", `Bool true) :: BaseConfigurationTest.dummy_base_json))
52+
~expected:{ dummy_analyze_configuration with dump_model_query_results = true };
53+
assert_parsed
54+
(`Assoc (("find_missing_flows", `String "obscure") :: BaseConfigurationTest.dummy_base_json))
55+
~expected:{ dummy_analyze_configuration with find_missing_flows = Some "obscure" };
56+
assert_parsed
57+
(`Assoc (("inline_decorators", `Bool true) :: BaseConfigurationTest.dummy_base_json))
58+
~expected:{ dummy_analyze_configuration with inline_decorators = true };
59+
assert_parsed
60+
(`Assoc (("maximum_tito_depth", `Int 5) :: BaseConfigurationTest.dummy_base_json))
61+
~expected:{ dummy_analyze_configuration with maximum_tito_depth = Some 5 };
62+
assert_parsed
63+
(`Assoc (("maximum_trace_length", `Int 5) :: BaseConfigurationTest.dummy_base_json))
64+
~expected:{ dummy_analyze_configuration with maximum_trace_length = Some 5 };
65+
assert_parsed
66+
(`Assoc (("no_verify", `Bool true) :: BaseConfigurationTest.dummy_base_json))
67+
~expected:{ dummy_analyze_configuration with no_verify = true };
68+
assert_parsed
69+
(`Assoc (("repository_root", `String "/root") :: BaseConfigurationTest.dummy_base_json))
70+
~expected:
71+
{ dummy_analyze_configuration with repository_root = Some (Path.create_absolute "/root") };
72+
assert_parsed
73+
(`Assoc (("rule_filter", `List [`Int 1; `Int 2]) :: BaseConfigurationTest.dummy_base_json))
74+
~expected:{ dummy_analyze_configuration with rule_filter = Some [1; 2] };
75+
assert_parsed
76+
(`Assoc (("save_results_to", `String "/result") :: BaseConfigurationTest.dummy_base_json))
77+
~expected:
78+
{ dummy_analyze_configuration with save_results_to = Some (Path.create_absolute "/result") };
79+
assert_parsed
80+
(`Assoc (("strict", `Bool true) :: BaseConfigurationTest.dummy_base_json))
81+
~expected:{ dummy_analyze_configuration with strict = true };
82+
assert_parsed
83+
(`Assoc
84+
(("taint_model_paths", `List [`String "/taint"; `String "/model"])
85+
:: BaseConfigurationTest.dummy_base_json))
86+
~expected:
87+
{
88+
dummy_analyze_configuration with
89+
taint_model_paths = [Path.create_absolute "/taint"; Path.create_absolute "/model"];
90+
};
91+
assert_parsed
92+
(`Assoc (("use_cache", `Bool true) :: BaseConfigurationTest.dummy_base_json))
93+
~expected:{ dummy_analyze_configuration with use_cache = true };
94+
()
95+
96+
97+
let () = "configuration" >::: ["json_parsing" >:: test_json_parsing] |> Test.run
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*)
7+
8+
(* Blank to catch unused tests *)

source/jsonParsing.ml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,20 @@ let bool_member ?default name json = member name json |> to_bool_with_default ?d
2626

2727
let int_member ?default name json = member name json |> to_int_with_default ?default
2828

29-
let optional_string_member name json =
29+
let optional_member ~f name json =
3030
member name json
3131
|> function
3232
| `Null -> None
33-
| _ as element -> Some (to_string element)
33+
| _ as element -> Some (f element)
3434

3535

36-
let path_member name json = member name json |> to_path
36+
let optional_string_member = optional_member ~f:to_string
3737

38-
let optional_path_member name json =
39-
member name json
40-
|> function
41-
| `Null -> None
42-
| _ as element -> Some (to_path element)
38+
let optional_int_member = optional_member ~f:to_int
39+
40+
let path_member name json = member name json |> to_path
4341

42+
let optional_path_member = optional_member ~f:to_path
4443

4544
let list_member ?default ~f name json =
4645
member name json

source/jsonParsing.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ val optional_string_member : string -> Yojson.Safe.t -> string option
8686
- If [json] is indeed a directory but [name] is not presented, return [None].
8787
- In all other cases, raise {!Yojson.Safe.Util.Type_error}. *)
8888

89+
val optional_int_member : string -> Yojson.Safe.t -> int option
90+
(** [optional_int_member name json] tries to look up key [name] in [json] as a dictionary.
91+
92+
- If [json] is indeed a dictionary and [name] is presented, convert the corresponding value to a
93+
int and return it.
94+
- If [json] is indeed a directory but [name] is not presented, return [None].
95+
- In all other cases, raise {!Yojson.Safe.Util.Type_error}. *)
96+
8997
val optional_path_member : string -> Yojson.Safe.t -> PyrePath.t option
9098
(** [optional_path_member name json] tries to look up key [name] in [json] as a dictionary.
9199

0 commit comments

Comments
 (0)