Skip to content

Commit 44b1e81

Browse files
Analysis refactor for #8425 (#8478)
* Omit empty document symbol children DocumentSymbol.children is optional, so only set it when a symbol has nested entries. This avoids serializing leaf symbols with an empty children array. Signed-off-by: Pedro Castro <aspeddro@gmail.com> * add transform_opt for server-side use * analysis: add state_to_yojson * packages: add dependencies * Update CHANGELOG.md * Apply codex review suggestions * Refactor cmt_viewer.dump for serve-side * Analysis cmt dump return a string instead a option --------- Signed-off-by: Pedro Castro <aspeddro@gmail.com> Co-authored-by: Florian Hammerschmidt <florianh89@gmail.com>
1 parent 863cf5f commit 44b1e81

9 files changed

Lines changed: 289 additions & 163 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
- Refactor analysis CLI helpers to use source input. https://github.com/rescript-lang/rescript/pull/8466
7070
- Include syntax, gentype, analysis, tools, and reanalyze tests in coverage reports. https://github.com/rescript-lang/rescript/pull/8467
7171
- Remove the unreachable `Longident.Lapply` constructor (OCaml's applicative-functor path syntax `F(X).t`, which ReScript's grammar cannot produce). https://github.com/rescript-lang/rescript/pull/8469
72+
- Refactor analysis for server side use. https://github.com/rescript-lang/rescript/pull/8478
7273
- Remove unused files. https://github.com/rescript-lang/rescript/pull/8481
7374

7475
# 13.0.0-alpha.4

analysis/bin/main.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ let main () =
221221
| [_; "format"; path] -> Cli.format ~path
222222
| [_; "test"; path] -> Cli.test ~state ~path
223223
| [_; "cmt"; rescript_json; cmt_path] ->
224-
Cmt_viewer.dump ~state rescript_json cmt_path
224+
Cli.dump_cmt ~state ~rescript_json ~cmt_path
225225
| args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help
226226
| _ ->
227227
prerr_endline help;

analysis/src/cli.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,25 @@ let create_interface ~path ~cmi_file =
182182
in
183183
Printf.printf "%s" result
184184

185+
let dump_cmt ~state ~rescript_json ~cmt_path =
186+
let uri = Uri.from_path (Filename.remove_extension cmt_path ^ ".res") in
187+
let package =
188+
let uri = Uri.from_path rescript_json in
189+
Packages.get_package ~state ~uri
190+
in
191+
match package with
192+
| None -> print_null ()
193+
| Some package -> (
194+
let module_name =
195+
Build_system.namespaced_name package.namespace
196+
(Find_files.get_name cmt_path)
197+
in
198+
match Cmt.full_for_cmt ~module_name ~package ~uri cmt_path with
199+
| None -> print_null ()
200+
| Some full ->
201+
let content = Cmt_viewer.dump ~full ~filter_for_position:None in
202+
Printf.printf "%s" content)
203+
185204
let test ~state ~path =
186205
Uri.strip_path := true;
187206
match Files.read_file path with

analysis/src/cmt_viewer.ml

Lines changed: 86 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -14,117 +14,99 @@ let filter_by_cursor cursor (loc : Warnings.loc) : bool =
1414
in
1515
line_in && col_in
1616

17-
type filter = Cursor of (int * int) | Loc of Loc.t
18-
19-
let dump ~state ?filter rescript_json cmt_path =
20-
let uri = Uri.from_path (Filename.remove_extension cmt_path ^ ".res") in
21-
let package =
22-
let uri = Uri.from_path rescript_json in
23-
Packages.get_package ~state ~uri |> Option.get
24-
in
25-
let module_name =
26-
Build_system.namespaced_name package.namespace
27-
(Find_files.get_name cmt_path)
17+
let dump ~(filter_for_position : (int * int) option) ~full =
18+
let open Shared_types in
19+
let open Shared_types.Stamps in
20+
let buffer = Buffer.create 4096 in
21+
let printf fmt = Printf.bprintf buffer fmt in
22+
let apply_filter =
23+
match filter_for_position with
24+
| None -> fun _ -> true
25+
| Some cursor -> Loc.has_pos ~pos:cursor
2826
in
29-
match Cmt.full_for_cmt ~module_name ~package ~uri cmt_path with
30-
| None -> failwith (Format.sprintf "Could not load cmt for %s" cmt_path)
31-
| Some full ->
32-
let open Shared_types in
33-
let open Shared_types.Stamps in
34-
let apply_filter =
35-
match filter with
36-
| None -> fun _ -> true
37-
| Some (Cursor cursor) -> Loc.has_pos ~pos:cursor
38-
| Some (Loc loc) -> Loc.is_inside loc
39-
in
40-
(match filter with
41-
| None -> ()
42-
| Some (Cursor (line, col)) ->
43-
Printf.printf "Filtering by cursor %d,%d\n" line col
44-
| Some (Loc loc) ->
45-
Printf.printf "Filtering by loc %s\n" (Loc.to_string loc));
27+
(match filter_for_position with
28+
| None -> ()
29+
| Some (line, col) -> printf "Filtering by cursor %d,%d\n" line col);
4630

47-
Printf.printf "file moduleName: %s\n\n" full.file.module_name;
31+
printf "file moduleName: %s\n\n" full.file.module_name;
4832

49-
let stamps =
50-
full.file.stamps |> get_entries
51-
|> List.filter (fun (_, stamp) -> apply_filter (loc_of_kind stamp))
52-
in
33+
let stamps =
34+
full.file.stamps |> get_entries
35+
|> List.filter (fun (_, stamp) -> apply_filter (loc_of_kind stamp))
36+
in
5337

54-
let total_stamps = List.length stamps in
55-
Printf.printf "Found %d stamps:\n%s" total_stamps
56-
(if total_stamps > 0 then "\n" else "");
38+
let total_stamps = List.length stamps in
39+
printf "Found %d stamps:\n%s" total_stamps
40+
(if total_stamps > 0 then "\n" else "");
5741

58-
stamps
59-
|> List.sort (fun (_, a) (_, b) ->
60-
let a_loc = loc_of_kind a in
61-
let b_loc = loc_of_kind b in
62-
match compare a_loc.loc_start.pos_lnum b_loc.loc_start.pos_lnum with
63-
| 0 -> compare a_loc.loc_start.pos_cnum b_loc.loc_start.pos_cnum
64-
| c -> c)
65-
|> List.iter (fun (stamp, kind) ->
66-
match kind with
67-
| KType t ->
68-
Printf.printf "%d ktype %s\n" stamp
69-
(Warnings.loc_to_string t.extent_loc)
70-
| KValue t ->
71-
Printf.printf "%d kvalue %s\n" stamp
72-
(Warnings.loc_to_string t.extent_loc)
73-
| KModule t ->
74-
Printf.printf "%d kmodule %s\n" stamp
75-
(Warnings.loc_to_string t.extent_loc)
76-
| KConstructor t ->
77-
Printf.printf "%d kconstructor %s\n" stamp
78-
(Warnings.loc_to_string t.extent_loc));
42+
stamps
43+
|> List.sort (fun (_, a) (_, b) ->
44+
let a_loc = loc_of_kind a in
45+
let b_loc = loc_of_kind b in
46+
match compare a_loc.loc_start.pos_lnum b_loc.loc_start.pos_lnum with
47+
| 0 -> compare a_loc.loc_start.pos_cnum b_loc.loc_start.pos_cnum
48+
| c -> c)
49+
|> List.iter (fun (stamp, kind) ->
50+
match kind with
51+
| KType t ->
52+
printf "%d ktype %s\n" stamp
53+
(Warnings.loc_to_string t.extent_loc)
54+
| KValue t ->
55+
printf "%d kvalue %s\n" stamp
56+
(Warnings.loc_to_string t.extent_loc)
57+
| KModule t ->
58+
printf "%d kmodule %s\n" stamp
59+
(Warnings.loc_to_string t.extent_loc)
60+
| KConstructor t ->
61+
printf "%d kconstructor %s\n" stamp
62+
(Warnings.loc_to_string t.extent_loc));
7963

80-
(* dump the structure *)
81-
let rec dump_structure indent (structure : Module.structure) =
82-
if indent > 0 then Printf.printf "%s" (String.make indent ' ');
83-
Printf.printf "Structure %s:\n" structure.name;
84-
structure.items |> List.iter (dump_structure_item (indent + 2))
85-
and dump_structure_item indent item =
86-
if indent > 0 then Printf.printf "%s" (String.make indent ' ');
87-
let open Module in
88-
match item.kind with
89-
| Value _typedExpr ->
90-
Printf.printf "Value %s %s\n" item.name
91-
(Warnings.loc_to_string item.loc)
92-
| Type _ ->
93-
Printf.printf "Type %s %s\n" item.name (Warnings.loc_to_string item.loc)
94-
| Module {type_ = m} ->
95-
Printf.printf "Module %s %s\n" item.name
96-
(Warnings.loc_to_string item.loc);
97-
dump_module indent m
98-
and dump_module indent (module_ : Module.t) =
99-
match module_ with
100-
| Ident path -> Printf.printf "Module (Ident) %s\n" (Path.name path)
101-
| Structure structure -> dump_structure indent structure
102-
| Constraint (m1, m2) ->
103-
dump_module indent m1;
104-
dump_module indent m2
105-
in
64+
(* dump the structure *)
65+
let rec dump_structure indent (structure : Module.structure) =
66+
if indent > 0 then printf "%s" (String.make indent ' ');
67+
printf "Structure %s:\n" structure.name;
68+
structure.items |> List.iter (dump_structure_item (indent + 2))
69+
and dump_structure_item indent item =
70+
if indent > 0 then printf "%s" (String.make indent ' ');
71+
let open Module in
72+
match item.kind with
73+
| Value _typedExpr ->
74+
printf "Value %s %s\n" item.name (Warnings.loc_to_string item.loc)
75+
| Type _ ->
76+
printf "Type %s %s\n" item.name (Warnings.loc_to_string item.loc)
77+
| Module {type_ = m} ->
78+
printf "Module %s %s\n" item.name (Warnings.loc_to_string item.loc);
79+
dump_module indent m
80+
and dump_module indent (module_ : Module.t) =
81+
match module_ with
82+
| Ident path -> printf "Module (Ident) %s\n" (Path.name path)
83+
| Structure structure -> dump_structure indent structure
84+
| Constraint (m1, m2) ->
85+
dump_module indent m1;
86+
dump_module indent m2
87+
in
10688

107-
print_newline ();
108-
dump_structure 0 full.file.structure;
89+
printf "\n";
90+
dump_structure 0 full.file.structure;
10991

110-
(* Dump all locItems (typed nodes) *)
111-
let loc_items =
112-
match full.extra with
113-
| {loc_items} ->
114-
loc_items |> List.filter (fun loc_item -> apply_filter loc_item.loc)
115-
in
92+
(* Dump all locItems (typed nodes) *)
93+
let loc_items =
94+
match full.extra with
95+
| {loc_items} ->
96+
loc_items |> List.filter (fun loc_item -> apply_filter loc_item.loc)
97+
in
11698

117-
Printf.printf "\nFound %d locItems (typed nodes):\n\n"
118-
(List.length loc_items);
99+
printf "\nFound %d locItems (typed nodes):\n\n" (List.length loc_items);
119100

120-
loc_items
121-
|> List.sort (fun a b ->
122-
let a_loc = a.loc.Location.loc_start in
123-
let b_loc = b.loc.Location.loc_start in
124-
match compare a_loc.pos_lnum b_loc.pos_lnum with
125-
| 0 -> compare a_loc.pos_cnum b_loc.pos_cnum
126-
| c -> c)
127-
|> List.iter (fun {loc; loc_type} ->
128-
let loc_str = Warnings.loc_to_string loc in
129-
let kind_str = Shared_types.loc_type_to_string loc_type in
130-
Printf.printf "%s %s\n" loc_str kind_str)
101+
loc_items
102+
|> List.sort (fun a b ->
103+
let a_loc = a.loc.Location.loc_start in
104+
let b_loc = b.loc.Location.loc_start in
105+
match compare a_loc.pos_lnum b_loc.pos_lnum with
106+
| 0 -> compare a_loc.pos_cnum b_loc.pos_cnum
107+
| c -> c)
108+
|> List.iter (fun {loc; loc_type} ->
109+
let loc_str = Warnings.loc_to_string loc in
110+
let kind_str = Shared_types.loc_type_to_string loc_type in
111+
printf "%s %s\n" loc_str kind_str);
112+
Buffer.contents buffer

analysis/src/codemod.ml

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,57 @@ let rec collect_patterns p =
55
| Ppat_or (p1, p2) -> collect_patterns p1 @ [p2]
66
| _ -> [p]
77

8-
let transform ~source ~pos ~debug ~typ ~hint =
9-
let structure, print_expr, _, _ = Xform.parse_implementation ~source in
10-
match typ with
11-
| AddMissingCases -> (
12-
let source = "let " ^ hint ^ " = ()" in
13-
let {Res_driver.parsetree = hint_structure} =
14-
Res_driver.parse_implementation_from_source ~for_printer:false
15-
~display_filename:"<none>" ~source
16-
in
17-
match hint_structure with
18-
| [{pstr_desc = Pstr_value (_, [{pvb_pat = pattern}])}] -> (
19-
let cases =
20-
collect_patterns pattern
21-
|> List.map (fun (p : Parsetree.pattern) ->
22-
Ast_helper.Exp.case p (Type_utils.Codegen.mk_fail_with_exp ()))
8+
let transform_opt ~source ~pos ~debug ~typ ~hint =
9+
let log message = if debug then print_endline message in
10+
try
11+
let structure, print_expr, _, _ = Xform.parse_implementation ~source in
12+
match typ with
13+
| AddMissingCases -> (
14+
let source = "let " ^ hint ^ " = ()" in
15+
let {Res_driver.parsetree = hint_structure} =
16+
Res_driver.parse_implementation_from_source ~for_printer:false
17+
~display_filename:"<none>" ~source
2318
in
24-
let result = ref None in
25-
let mk_iterator ~pos ~result =
26-
let expr (iterator : Ast_iterator.iterator) (exp : Parsetree.expression)
27-
=
28-
match exp.pexp_desc with
29-
| Pexp_match (e, existing_cases)
30-
when Pos.of_lexing exp.pexp_loc.loc_start = pos ->
31-
result :=
32-
Some {exp with pexp_desc = Pexp_match (e, existing_cases @ cases)}
33-
| _ -> Ast_iterator.default_iterator.expr iterator exp
19+
match hint_structure with
20+
| [{pstr_desc = Pstr_value (_, [{pvb_pat = pattern}])}] -> (
21+
let cases =
22+
collect_patterns pattern
23+
|> List.map (fun (p : Parsetree.pattern) ->
24+
Ast_helper.Exp.case p (Type_utils.Codegen.mk_fail_with_exp ()))
3425
in
35-
{Ast_iterator.default_iterator with expr}
36-
in
37-
let iterator = mk_iterator ~pos ~result in
38-
iterator.structure iterator structure;
39-
match !result with
40-
| None ->
41-
if debug then print_endline "Found no result";
42-
exit 1
43-
| Some switch_expr ->
44-
print_expr ~range:(Loc.range_of_loc switch_expr.pexp_loc) switch_expr)
45-
| _ ->
46-
if debug then print_endline "Mismatch in expected structure";
47-
exit 1)
26+
let result = ref None in
27+
let mk_iterator ~pos ~result =
28+
let expr (iterator : Ast_iterator.iterator)
29+
(exp : Parsetree.expression) =
30+
match exp.pexp_desc with
31+
| Pexp_match (e, existing_cases)
32+
when Pos.of_lexing exp.pexp_loc.loc_start = pos ->
33+
result :=
34+
Some
35+
{exp with pexp_desc = Pexp_match (e, existing_cases @ cases)}
36+
| _ -> Ast_iterator.default_iterator.expr iterator exp
37+
in
38+
{Ast_iterator.default_iterator with expr}
39+
in
40+
let iterator = mk_iterator ~pos ~result in
41+
iterator.structure iterator structure;
42+
match !result with
43+
| None ->
44+
log "Found no result";
45+
None
46+
| Some switch_expr ->
47+
Some
48+
(print_expr
49+
~range:(Loc.range_of_loc switch_expr.pexp_loc)
50+
switch_expr))
51+
| _ ->
52+
log "Mismatch in expected structure";
53+
None)
54+
with exn ->
55+
log ("Codemod failed: " ^ Printexc.to_string exn);
56+
None
57+
58+
let transform ~source ~pos ~debug ~typ ~hint =
59+
match transform_opt ~source ~pos ~debug ~typ ~hint with
60+
| Some result -> result
61+
| None -> exit 1

analysis/src/document_symbol.ml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ let get_symbols ~source ~kind_file =
1010
then
1111
let range = Utils.cmt_loc_to_range loc in
1212
let symbol =
13-
Lsp.Types.DocumentSymbol.create ~name ~range ~selectionRange:range
14-
~children:[] ~kind ()
13+
Lsp.Types.DocumentSymbol.create ~name ~range ~selectionRange:range ~kind
14+
()
1515
in
1616
symbols := symbol :: !symbols
1717
in
@@ -165,13 +165,14 @@ let get_symbols ~source ~kind_file =
165165
| [] -> [symbol]
166166
| last :: rest ->
167167
if is_inside symbol last then
168-
match last.children with
169-
| Some c ->
170-
let new_last =
171-
{last with children = Some (c |> add_symbol_to_children ~symbol)}
172-
in
173-
new_last :: rest
174-
| _ -> rest
168+
let children = last.children |> Option.value ~default:[] in
169+
let new_last =
170+
{
171+
last with
172+
children = Some (children |> add_symbol_to_children ~symbol);
173+
}
174+
in
175+
new_last :: rest
175176
else symbol :: children
176177
in
177178
let rec add_sorted_symbols_to_children ~sorted_symbols children =

0 commit comments

Comments
 (0)