Source file Normalize_std_ast.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
open Parser_standard
open Std_ast
let is_doc = function
| {attr_name= {Location.txt= "ocaml.doc" | "ocaml.text"; _}; _} -> true
| _ -> false
let dedup_cmts fragment ast =
let of_ast ast =
let docs = ref (Set.empty (module Cmt)) in
let attribute m atr =
match atr with
| { attr_payload=
PStr
[ { pstr_desc=
Pstr_eval
( { pexp_desc=
Pexp_constant (Pconst_string (doc, _, None))
; pexp_loc
; _ }
, [] )
; _ } ]
; _ }
when is_doc atr ->
docs := Set.add !docs (Cmt.create ("*" ^ doc) pexp_loc) ;
atr
| _ -> Ast_mapper.default_mapper.attribute m atr
in
map fragment {Ast_mapper.default_mapper with attribute} ast |> ignore ;
!docs
in
Set.(to_list (diff (of_list (module Cmt) comments) (of_ast ast)))
let normalize_code conf (m : Ast_mapper.mapper) txt =
match Parse_with_comments.parse Parse.ast Structure conf ~source:txt with
| {ast; ; _} ->
let = dedup_cmts Structure ast comments in
let fmt (l : Cmt.t list) =
List.sort l ~compare:(fun {Cmt.loc= a; _} {Cmt.loc= b; _} ->
Migrate_ast.Location.compare a b )
|> List.iter ~f:(fun {Cmt.txt; _} -> Format.fprintf fmt "%s," txt)
in
let ast = m.structure m ast in
Format.asprintf "AST,%a,COMMENTS,[%a]" Printast.implementation ast
print_comments comments
| exception _ -> txt
let docstring (c : Conf.t) =
Docstring.normalize ~parse_docstrings:c.fmt_opts.parse_docstrings
let sort_attributes : attributes -> attributes =
List.sort ~compare:Poly.compare
let make_mapper conf ~ =
let open Ast_helper in
let location _ _ = Location.none in
let attribute (m : Ast_mapper.mapper) (attr : attribute) =
match attr.attr_payload with
| PStr
[ ( { pstr_desc=
Pstr_eval
( ( { pexp_desc=
Pexp_constant (Pconst_string (doc, str_loc, None))
; _ } as exp )
, [] )
; _ } as pstr ) ]
when is_doc attr ->
let normalize_code = normalize_code conf m in
let doc' = docstring conf ~normalize_code doc in
Ast_mapper.default_mapper.attribute m
{ attr with
attr_payload=
PStr
[ { pstr with
pstr_desc=
Pstr_eval
( { exp with
pexp_desc=
Pexp_constant
(Pconst_string (doc', str_loc, None))
; pexp_loc_stack= [] }
, [] ) } ] }
| _ -> Ast_mapper.default_mapper.attribute m attr
in
let attributes (m : Ast_mapper.mapper) (atrs : attribute list) =
let atrs =
if ignore_doc_comments then
List.filter atrs ~f:(fun a -> not (is_doc a))
else atrs
in
Ast_mapper.default_mapper.attributes m (sort_attributes atrs)
in
let expr (m : Ast_mapper.mapper) exp =
let exp = {exp with pexp_loc_stack= []} in
let {pexp_desc; pexp_loc= loc1; pexp_attributes= attrs1; _} = exp in
match pexp_desc with
| Pexp_poly ({pexp_desc= Pexp_constraint (e, t); _}, None) ->
m.expr m {exp with pexp_desc= Pexp_poly (e, Some t)}
| Pexp_constraint (e, {ptyp_desc= Ptyp_poly ([], _t); _}) -> m.expr m e
| Pexp_sequence
( exp1
, { pexp_desc= Pexp_sequence (exp2, exp3)
; pexp_loc= loc2
; pexp_attributes= attrs2
; _ } ) ->
m.expr m
(Exp.sequence ~loc:loc1 ~attrs:attrs1
(Exp.sequence ~loc:loc2 ~attrs:attrs2 exp1 exp2)
exp3 )
| _ -> Ast_mapper.default_mapper.expr m exp
in
let pat (m : Ast_mapper.mapper) pat =
let pat = {pat with ppat_loc_stack= []} in
let {ppat_desc; ppat_loc= loc1; ppat_attributes= attrs1; _} = pat in
match ppat_desc with
| Ppat_or
( pat1
, { ppat_desc= Ppat_or (pat2, pat3)
; ppat_loc= loc2
; ppat_attributes= attrs2
; _ } ) ->
m.pat m
(Pat.or_ ~loc:loc1 ~attrs:attrs1
(Pat.or_ ~loc:loc2 ~attrs:attrs2 pat1 pat2)
pat3 )
| _ -> Ast_mapper.default_mapper.pat m pat
in
let typ (m : Ast_mapper.mapper) typ =
let typ = {typ with ptyp_loc_stack= []} in
Ast_mapper.default_mapper.typ m typ
in
{ Ast_mapper.default_mapper with
location
; attribute
; attributes
; expr
; pat
; typ }
let ast fragment ~ c =
map fragment (make_mapper c ~ignore_doc_comments)
let equal fragment ~ c ast1 ast2 =
let map = ast fragment c ~ignore_doc_comments in
equal fragment (map ast1) (map ast2)
let ast = ast ~ignore_doc_comments:false
let make_docstring_mapper docstrings =
let attribute (m : Ast_mapper.mapper) attr =
match (attr.attr_name, attr.attr_payload) with
| ( {txt= "ocaml.doc" | "ocaml.text"; loc}
, PStr
[ { pstr_desc=
Pstr_eval
( { pexp_desc= Pexp_constant (Pconst_string (doc, _, None))
; _ }
, [] )
; _ } ] ) ->
docstrings := (loc, doc) :: !docstrings ;
attr
| _ -> Ast_mapper.default_mapper.attribute m attr
in
let attributes (m : Ast_mapper.mapper) atrs =
let atrs = List.filter atrs ~f:is_doc in
Ast_mapper.default_mapper.attributes m (sort_attributes atrs)
in
{Ast_mapper.default_mapper with attribute; attributes}
let docstrings (type a) (fragment : a t) s =
let docstrings = ref [] in
let (_ : a) = map fragment (make_docstring_mapper docstrings) s in
!docstrings
let docstring conf =
let mapper = make_mapper conf ~ignore_doc_comments:false in
let normalize_code = normalize_code conf mapper in
docstring conf ~normalize_code
let moved_docstrings fragment c s1 s2 =
let d1 = docstrings fragment s1 in
let d2 = docstrings fragment s2 in
let equal (_, x) (_, y) = String.equal (docstring c x) (docstring c y) in
match List.zip d1 d2 with
| Unequal_lengths ->
let l1 = List.filter d1 ~f:(fun x -> not (List.mem ~equal d2 x)) in
let l1 = List.map ~f:(fun (loc, x) -> Docstring.Removed (loc, x)) l1 in
let l2 = List.filter d2 ~f:(fun x -> not (List.mem ~equal d1 x)) in
let l2 = List.map ~f:(fun (loc, x) -> Docstring.Added (loc, x)) l2 in
List.rev_append l1 l2
| Ok l ->
let l = List.filter l ~f:(fun (x, y) -> not (equal x y)) in
List.map
~f:(fun ((loc, x), (_, y)) -> Docstring.Unstable (loc, x, y))
l