Skip to content

Commit bc91e42

Browse files
Attributes now stay where the user put them (#2451)
module [@attr] M = struct end used to be formatted into module = struct end [@@attr] This PR make it so that the user can choose between the two syntaxes, ocamlformat will not change anything.
1 parent 940eea5 commit bc91e42

18 files changed

+253
-180
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ profile. This started with version 0.26.0.
134134
- JaneStreet profile: doesn't align infix ops with open paren (#2204, @gpetiot)
135135
- Re-use the type let_binding from the parser instead of value_binding, improve the spacing of let-bindings regarding of having extension or comments (#2219, @gpetiot)
136136
- The `ocamlformat` package now only contains the binary, the library is available through the `ocamlformat-lib` package (#2230, @gpetiot)
137+
- The position of module and module type attributes is now preserved. (#2451, @emiletrotignon)
137138

138139
### Added
139140

lib/Ast.ml

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ module Ext = struct
105105
end
106106
end
107107

108+
module Ext_attrs = struct
109+
let has_doc ea =
110+
List.exists ~f:Attr.is_doc ea.attrs_before
111+
|| List.exists ~f:Attr.is_doc ea.attrs_after
112+
end
113+
108114
module Exp = struct
109115
let location x = x.pexp_loc
110116

@@ -318,27 +324,30 @@ module Structure_item = struct
318324
let has_doc itm =
319325
match itm.pstr_desc with
320326
| Pstr_attribute atr -> Attr.is_doc atr
327+
(* one attribute list *)
321328
| Pstr_eval (_, atrs)
322329
|Pstr_value {pvbs_bindings= {pvb_attributes= atrs; _} :: _; _}
323330
|Pstr_primitive {pval_attributes= atrs; _}
324331
|Pstr_type (_, {ptype_attributes= atrs; _} :: _)
325332
|Pstr_typext {ptyext_attributes= atrs; _}
326333
|Pstr_recmodule ({pmb_expr= {pmod_attributes= atrs; _}; _} :: _)
327-
|Pstr_modtype {pmtd_attributes= atrs; _}
328334
|Pstr_open {popen_attributes= atrs; _}
329335
|Pstr_extension (_, atrs)
330336
|Pstr_class_type ({pci_attributes= atrs; _} :: _)
331337
|Pstr_class ({pci_attributes= atrs; _} :: _) ->
332338
List.exists ~f:Attr.is_doc atrs
333-
| Pstr_include
334-
{pincl_mod= {pmod_attributes= atrs1; _}; pincl_attributes= atrs2; _}
335-
|Pstr_exception
339+
| Pstr_exception
336340
{ ptyexn_attributes= atrs1
337341
; ptyexn_constructor= {pext_attributes= atrs2; _}
338342
; _ }
339-
|Pstr_module
340-
{pmb_attributes= atrs1; pmb_expr= {pmod_attributes= atrs2; _}; _} ->
343+
|Pstr_include
344+
{pincl_mod= {pmod_attributes= atrs1; _}; pincl_attributes= atrs2; _}
345+
->
341346
List.exists ~f:Attr.is_doc atrs1 || List.exists ~f:Attr.is_doc atrs2
347+
| Pstr_modtype {pmtd_ext_attrs; _} -> Ext_attrs.has_doc pmtd_ext_attrs
348+
| Pstr_module {pmb_ext_attrs; pmb_expr= {pmod_attributes; _}; _} ->
349+
Ext_attrs.has_doc pmb_ext_attrs
350+
|| List.exists ~f:Attr.is_doc pmod_attributes
342351
| Pstr_value {pvbs_bindings= []; _}
343352
|Pstr_type (_, [])
344353
|Pstr_recmodule []
@@ -412,30 +421,33 @@ module Signature_item = struct
412421
let has_doc itm =
413422
match itm.psig_desc with
414423
| Psig_attribute atr -> Attr.is_doc atr
424+
(* one attribute list *)
415425
| Psig_value {pval_attributes= atrs; _}
416426
|Psig_type (_, {ptype_attributes= atrs; _} :: _)
417427
|Psig_typesubst ({ptype_attributes= atrs; _} :: _)
418428
|Psig_typext {ptyext_attributes= atrs; _}
419-
|Psig_modtype {pmtd_attributes= atrs; _}
420-
|Psig_modtypesubst {pmtd_attributes= atrs; _}
421-
|Psig_modsubst {pms_attributes= atrs; _}
422429
|Psig_open {popen_attributes= atrs; _}
423430
|Psig_extension (_, atrs)
424431
|Psig_class_type ({pci_attributes= atrs; _} :: _)
425432
|Psig_class ({pci_attributes= atrs; _} :: _) ->
426433
List.exists ~f:Attr.is_doc atrs
427-
| Psig_recmodule
428-
( {pmd_type= {pmty_attributes= atrs1; _}; pmd_attributes= atrs2; _}
429-
:: _ )
430-
|Psig_include
434+
(* two attribute list *)
435+
| Psig_modtype {pmtd_ext_attrs= ea; _}
436+
|Psig_modtypesubst {pmtd_ext_attrs= ea; _}
437+
|Psig_modsubst {pms_ext_attrs= ea; _} ->
438+
Ext_attrs.has_doc ea
439+
| Psig_include
431440
{pincl_mod= {pmty_attributes= atrs1; _}; pincl_attributes= atrs2; _}
432441
|Psig_exception
433442
{ ptyexn_attributes= atrs1
434443
; ptyexn_constructor= {pext_attributes= atrs2; _}
435-
; _ }
436-
|Psig_module
437-
{pmd_attributes= atrs1; pmd_type= {pmty_attributes= atrs2; _}; _} ->
444+
; _ } ->
438445
List.exists ~f:Attr.is_doc atrs1 || List.exists ~f:Attr.is_doc atrs2
446+
| Psig_recmodule
447+
({pmd_type= {pmty_attributes= atrs; _}; pmd_ext_attrs= ea; _} :: _)
448+
|Psig_module {pmd_ext_attrs= ea; pmd_type= {pmty_attributes= atrs; _}; _}
449+
->
450+
Ext_attrs.has_doc ea || (List.exists ~f:Attr.is_doc) atrs
439451
| Psig_type (_, [])
440452
|Psig_typesubst []
441453
|Psig_recmodule []
@@ -510,7 +522,7 @@ module Lb = struct
510522
end
511523

512524
module Mb = struct
513-
let has_doc itm = List.exists ~f:Attr.is_doc itm.pmb_attributes
525+
let has_doc itm = Ext_attrs.has_doc itm.pmb_ext_attrs
514526

515527
let is_simple (i, (c : Conf.t)) =
516528
Poly.(c.fmt_opts.module_item_spacing.v = `Compact)
@@ -524,7 +536,7 @@ module Mb = struct
524536
end
525537

526538
module Md = struct
527-
let has_doc itm = List.exists ~f:Attr.is_doc itm.pmd_attributes
539+
let has_doc itm = Ext_attrs.has_doc itm.pmd_ext_attrs
528540

529541
let is_simple (i, (c : Conf.t)) =
530542
Poly.(c.fmt_opts.module_item_spacing.v = `Compact)
@@ -669,6 +681,8 @@ include T
669681

670682
let is_top = function Top -> true | _ -> false
671683

684+
let attrs_of_ext_attrs ea = ea.attrs_before @ ea.attrs_after
685+
672686
let attributes = function
673687
| Pld _ -> []
674688
| Typ x -> x.ptyp_attributes
@@ -677,8 +691,8 @@ let attributes = function
677691
| Pat x -> x.ppat_attributes
678692
| Exp x -> x.pexp_attributes
679693
| Lb x -> x.pvb_attributes
680-
| Mb x -> x.pmb_attributes
681-
| Md x -> x.pmd_attributes
694+
| Mb x -> attrs_of_ext_attrs x.pmb_ext_attrs
695+
| Md x -> attrs_of_ext_attrs x.pmd_ext_attrs
682696
| Cl x -> x.pcl_attributes
683697
| Mty x -> x.pmty_attributes
684698
| Mod x -> x.pmod_attributes

lib/Fmt_ast.ml

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ let update_config_maybe_disabled c loc l f =
178178
let c = update_config c l in
179179
maybe_disabled c loc l f
180180

181+
let update_config_maybe_disabled_attrs c loc attrs f =
182+
let l = attrs.attrs_before @ attrs.attrs_after in
183+
update_config_maybe_disabled c loc l f
184+
181185
let update_config_maybe_disabled_block c loc l f =
182186
let fmt bdy = {empty with opn= Some (open_vbox 2); bdy; cls= close_box} in
183187
let c = update_config c l in
@@ -463,6 +467,15 @@ let fmt_docstring_around_item ?is_val ?force_before ?fit c attrs =
463467
in
464468
(doc_before, doc_after, attrs)
465469

470+
(** Returns the documentation before and after the item as well as the
471+
[ext_attrs] before and after attributes, modified.
472+
It is assumed that docstrings can only occurs in [attrs_after]. *)
473+
let fmt_docstring_around_item_attrs ?is_val ?force_before ?fit c attrs =
474+
let doc_before, doc_after, attrs_after =
475+
fmt_docstring_around_item ?is_val ?force_before ?fit c attrs.attrs_after
476+
in
477+
(doc_before, doc_after, attrs.attrs_before, attrs_after)
478+
466479
let fmt_extension_suffix c ext =
467480
opt ext (fun name -> str "%" $ fmt_str_loc c name)
468481

@@ -2289,7 +2302,9 @@ and fmt_expression c ?(box = true) ?pro ?epi ?eol ?parens ?(indent_wrap = 0)
22892302
c.conf
22902303
( hvbox 2
22912304
(fmt_module c ctx keyword ~eqty:":" name args (Some xbody)
2292-
xmty [] ~epi:(str "in") ~can_sparse ?ext ~rec_flag:false )
2305+
xmty
2306+
~attrs:(Ast_helper.Attr.ext_attrs ?ext ())
2307+
~epi:(str "in") ~can_sparse ~rec_flag:false )
22932308
$ fmt "@;<1000 0>"
22942309
$ fmt_expression c (sub_exp ~ctx exp) )
22952310
$ fmt_atrs )
@@ -3622,20 +3637,16 @@ and fmt_signature_item c ?ext {ast= si; _} =
36223637
$ esp $ fmt_opt epi
36233638
$ fmt_item_attributes c ~pre:(Break (1, 0)) atrs )
36243639
$ doc_after )
3625-
| Psig_modtype mtd -> fmt_module_type_declaration ?ext c ctx mtd
3626-
| Psig_modtypesubst mtd ->
3627-
fmt_module_type_declaration ?ext ~eqty:":=" c ctx mtd
3640+
| Psig_modtype mtd -> fmt_module_type_declaration c ctx mtd
3641+
| Psig_modtypesubst mtd -> fmt_module_type_declaration ~eqty:":=" c ctx mtd
36283642
| Psig_module md ->
36293643
hvbox 0
3630-
(fmt_module_declaration ?ext c ~rec_flag:false ~first:true
3644+
(fmt_module_declaration c ~rec_flag:false ~first:true
36313645
(sub_md ~ctx md) )
3632-
| Psig_modsubst ms -> hvbox 0 (fmt_module_substitution ?ext c ctx ms)
3646+
| Psig_modsubst ms -> hvbox 0 (fmt_module_substitution c ctx ms)
36333647
| Psig_open od -> fmt_open_description ?ext c ~kw_attributes:[] od
36343648
| Psig_recmodule mds ->
3635-
fmt_recmodule c ctx mds
3636-
(fmt_module_declaration ?ext)
3637-
(fun x -> Md x)
3638-
sub_md
3649+
fmt_recmodule c ctx mds fmt_module_declaration (fun x -> Md x) sub_md
36393650
| Psig_type (rec_flag, decls) -> fmt_type c ?ext rec_flag decls ctx
36403651
| Psig_typext te -> fmt_type_extension ?ext c ctx te
36413652
| Psig_value vd -> fmt_value_description ?ext c ctx vd
@@ -3720,8 +3731,9 @@ and fmt_class_exprs ?ext c ctx cls =
37203731
$ hovbox 0
37213732
@@ Cmts.fmt c cl.pci_loc (doc_before $ class_exprs $ doc_after) )
37223733

3723-
and fmt_module c ctx ?rec_ ?ext ?epi ?(can_sparse = false) keyword
3724-
?(eqty = "=") name xargs xbody xmty attributes ~rec_flag =
3734+
and fmt_module c ctx ?rec_ ?epi ?(can_sparse = false) keyword ?(eqty = "=")
3735+
name xargs xbody xmty ~attrs ~rec_flag =
3736+
let ext = attrs.attrs_extension in
37253737
let blk_t =
37263738
Option.value_map xmty ~default:empty ~f:(fun xmty ->
37273739
let blk = fmt_module_type ?rec_ c xmty in
@@ -3763,24 +3775,25 @@ and fmt_module c ctx ?rec_ ?ext ?epi ?(can_sparse = false) keyword
37633775
let bdy, epi = fmt_arg ~pro hd in
37643776
bdy $ fmt_args ~pro:epi tl
37653777
in
3766-
let intro =
3767-
str keyword
3768-
$ fmt_extension_suffix c ext
3769-
$ fmt_if rec_flag " rec" $ str " " $ fmt_str_loc_opt c name
3770-
in
37713778
let single_line =
37723779
Option.for_all xbody ~f:(fun x -> Mod.is_simple x.ast)
37733780
&& Option.for_all xmty ~f:(fun x -> Mty.is_simple x.ast)
37743781
&& List.for_all xargs ~f:(function {txt= Unit; _} -> true | _ -> false)
37753782
in
3783+
let doc_before, doc_after, attrs_before, attrs_after =
3784+
fmt_docstring_around_item_attrs c ~force_before:(not single_line)
3785+
~fit:true attrs
3786+
in
3787+
let intro =
3788+
str keyword
3789+
$ fmt_extension_suffix c ext
3790+
$ fmt_attributes c ~pre:(Break (1, 0)) attrs_before
3791+
$ fmt_if rec_flag " rec" $ str " " $ fmt_str_loc_opt c name
3792+
in
37763793
let compact =
37773794
Poly.(c.conf.fmt_opts.let_module.v = `Compact) || not can_sparse
37783795
in
37793796
let fmt_pro = opt blk_b.pro (fun pro -> fmt "@ " $ pro) in
3780-
let doc_before, doc_after, atrs =
3781-
fmt_docstring_around_item c ~force_before:(not single_line) ~fit:true
3782-
attributes
3783-
in
37843797
hvbox
37853798
(if compact then 0 else 2)
37863799
( doc_before
@@ -3799,7 +3812,7 @@ and fmt_module c ctx ?rec_ ?ext ?epi ?(can_sparse = false) keyword
37993812
$ fmt_if (Option.is_none blk_b.pro && Option.is_some xbody) "@ "
38003813
$ blk_b.bdy )
38013814
$ blk_b.esp $ fmt_opt blk_b.epi
3802-
$ fmt_item_attributes c ~pre:(Break (1, 0)) atrs
3815+
$ fmt_item_attributes c ~pre:(Break (1, 0)) attrs_after
38033816
$ doc_after
38043817
$ opt epi (fun epi ->
38053818
fmt_or_k compact
@@ -3810,26 +3823,25 @@ and fmt_module c ctx ?rec_ ?ext ?epi ?(can_sparse = false) keyword
38103823
(fmt "@;<1 -2>")
38113824
$ epi ) )
38123825

3813-
and fmt_module_declaration ?ext c ~rec_flag ~first {ast= pmd; _} =
3826+
and fmt_module_declaration c ~rec_flag ~first {ast= pmd; _} =
38143827
protect c (Md pmd)
38153828
@@
3816-
let {pmd_name; pmd_args; pmd_type; pmd_attributes; pmd_loc} = pmd in
3817-
update_config_maybe_disabled c pmd_loc pmd_attributes
3829+
let {pmd_name; pmd_args; pmd_type; pmd_ext_attrs= attrs; pmd_loc} = pmd in
3830+
update_config_maybe_disabled_attrs c pmd_loc attrs
38183831
@@ fun c ->
38193832
let ctx = Md pmd in
3820-
let ext = if first then ext else None in
38213833
let keyword = if first then "module" else "and" in
38223834
let xmty = sub_mty ~ctx pmd_type in
38233835
let eqty =
38243836
match xmty.ast.pmty_desc with Pmty_alias _ -> None | _ -> Some ":"
38253837
in
38263838
Cmts.fmt c pmd_loc
3827-
(fmt_module ~rec_:rec_flag ?ext c ctx keyword pmd_name pmd_args None
3828-
?eqty (Some xmty) ~rec_flag:(rec_flag && first) pmd_attributes )
3839+
(fmt_module ~rec_:rec_flag c ctx keyword pmd_name pmd_args None ?eqty
3840+
(Some xmty) ~rec_flag:(rec_flag && first) ~attrs )
38293841

3830-
and fmt_module_substitution ?ext c ctx pms =
3831-
let {pms_name; pms_manifest; pms_attributes; pms_loc} = pms in
3832-
update_config_maybe_disabled c pms_loc pms_attributes
3842+
and fmt_module_substitution c ctx pms =
3843+
let {pms_name; pms_manifest; pms_ext_attrs= attrs; pms_loc} = pms in
3844+
update_config_maybe_disabled_attrs c pms_loc attrs
38333845
@@ fun c ->
38343846
let xmty =
38353847
(* TODO: improve *)
@@ -3840,17 +3852,17 @@ and fmt_module_substitution ?ext c ctx pms =
38403852
in
38413853
let pms_name = {pms_name with txt= Some pms_name.txt} in
38423854
Cmts.fmt c pms_loc
3843-
(fmt_module ?ext c ctx "module" ~eqty:":=" pms_name [] None (Some xmty)
3844-
pms_attributes ~rec_flag:false )
3855+
(fmt_module c ctx "module" ~eqty:":=" pms_name [] None (Some xmty) ~attrs
3856+
~rec_flag:false )
38453857

3846-
and fmt_module_type_declaration ?ext ?eqty c ctx pmtd =
3847-
let {pmtd_name; pmtd_type; pmtd_attributes; pmtd_loc} = pmtd in
3848-
update_config_maybe_disabled c pmtd_loc pmtd_attributes
3858+
and fmt_module_type_declaration ?eqty c ctx pmtd =
3859+
let {pmtd_name; pmtd_type; pmtd_ext_attrs= attrs; pmtd_loc} = pmtd in
3860+
update_config_maybe_disabled_attrs c pmtd_loc attrs
38493861
@@ fun c ->
38503862
let pmtd_name = {pmtd_name with txt= Some pmtd_name.txt} in
3851-
fmt_module ?ext ?eqty c ctx "module type" pmtd_name [] None ~rec_flag:false
3863+
fmt_module ?eqty c ctx "module type" pmtd_name [] None ~rec_flag:false
38523864
(Option.map pmtd_type ~f:(sub_mty ~ctx))
3853-
pmtd_attributes
3865+
~attrs
38543866

38553867
and fmt_open_description ?ext c ?(keyword = "open") ~kw_attributes
38563868
{popen_expr= popen_lid; popen_override; popen_attributes; popen_loc} =
@@ -3909,13 +3921,15 @@ and fmt_with_constraint c ctx ~pre = function
39093921
let m1 = {m1 with txt= Some (str_longident m1.txt)} in
39103922
let m2 = Some (sub_mty ~ctx m2) in
39113923
str pre $ break 1 2
3912-
$ fmt_module c ctx "module type" m1 [] None ~rec_flag:false m2 []
3924+
$ fmt_module c ctx "module type" m1 [] None ~rec_flag:false m2
3925+
~attrs:(Ast_helper.Attr.ext_attrs ())
39133926
| Pwith_modtypesubst (m1, m2) ->
39143927
let m1 = {m1 with txt= Some (str_longident m1.txt)} in
39153928
let m2 = Some (sub_mty ~ctx m2) in
39163929
str pre $ break 1 2
39173930
$ fmt_module c ctx ~eqty:":=" "module type" m1 [] None ~rec_flag:false
3918-
m2 []
3931+
m2
3932+
~attrs:(Ast_helper.Attr.ext_attrs ())
39193933

39203934
and fmt_mod_apply c ctx loc attrs ~parens ~dock_struct me_f arg =
39213935
match me_f.pmod_desc with
@@ -4184,7 +4198,7 @@ and fmt_structure_item c ~last:last_item ?ext ~semisemi
41844198
let keyword = str "include" $ fmt_extension_suffix c ext $ fmt "@ " in
41854199
fmt_module_statement c ~attributes ~keyword (sub_mod ~ctx pincl_mod)
41864200
| Pstr_module mb ->
4187-
fmt_module_binding ?ext c ~rec_flag:false ~first:true (sub_mb ~ctx mb)
4201+
fmt_module_binding c ~rec_flag:false ~first:true (sub_mb ~ctx mb)
41884202
| Pstr_open
41894203
{popen_expr; popen_override; popen_attributes= attributes; popen_loc}
41904204
->
@@ -4201,9 +4215,7 @@ and fmt_structure_item c ~last:last_item ?ext ~semisemi
42014215
fmt_module_statement c ~attributes ~keyword (sub_mod ~ctx popen_expr)
42024216
| Pstr_primitive vd -> fmt_value_description ?ext c ctx vd
42034217
| Pstr_recmodule mbs ->
4204-
fmt_recmodule c ctx mbs (fmt_module_binding ?ext)
4205-
(fun x -> Mb x)
4206-
sub_mb
4218+
fmt_recmodule c ctx mbs fmt_module_binding (fun x -> Mb x) sub_mb
42074219
| Pstr_type (rec_flag, decls) -> fmt_type c ?ext rec_flag decls ctx
42084220
| Pstr_typext te -> fmt_type_extension ?ext c ctx te
42094221
| Pstr_value {pvbs_rec= rec_flag; pvbs_bindings= bindings; pvbs_extension}
@@ -4228,7 +4240,7 @@ and fmt_structure_item c ~last:last_item ?ext ~semisemi
42284240
fmt_value_binding c ~rec_flag ?ext ?epi b
42294241
in
42304242
fmt_item_list c ctx update_config ast fmt_item bindings
4231-
| Pstr_modtype mtd -> fmt_module_type_declaration ?ext c ctx mtd
4243+
| Pstr_modtype mtd -> fmt_module_type_declaration c ctx mtd
42324244
| Pstr_extension (ext, atrs) ->
42334245
let doc_before, doc_after, atrs = fmt_docstring_around_item c atrs in
42344246
let box =
@@ -4376,12 +4388,12 @@ and fmt_value_binding c ~rec_flag ?ext ?in_ ?epi
43764388
$ epi )
43774389
$ fmt_docstring c ~pro:(fmt "@\n") doc2
43784390

4379-
and fmt_module_binding ?ext c ~rec_flag ~first {ast= pmb; _} =
4391+
and fmt_module_binding c ~rec_flag ~first {ast= pmb; _} =
4392+
let {pmb_name; pmb_ext_attrs= attrs; _} = pmb in
43804393
protect c (Mb pmb)
4381-
@@ update_config_maybe_disabled c pmb.pmb_loc pmb.pmb_attributes
4394+
@@ update_config_maybe_disabled_attrs c pmb.pmb_loc attrs
43824395
@@ fun c ->
43834396
let ctx = Mb pmb in
4384-
let ext = if first then ext else None in
43854397
let keyword = if first then "module" else "and" in
43864398
let xbody = sub_mod ~ctx pmb.pmb_expr in
43874399
let xbody, xmty =
@@ -4395,9 +4407,8 @@ and fmt_module_binding ?ext c ~rec_flag ~first {ast= pmb; _} =
43954407
| _ -> (xbody, None)
43964408
in
43974409
Cmts.fmt c pmb.pmb_loc
4398-
(fmt_module ~rec_:rec_flag ?ext c ctx keyword
4399-
~rec_flag:(rec_flag && first) ~eqty:":" pmb.pmb_name pmb.pmb_args
4400-
(Some xbody) xmty pmb.pmb_attributes )
4410+
(fmt_module ~rec_:rec_flag c ctx keyword ~rec_flag:(rec_flag && first)
4411+
~eqty:":" pmb_name pmb.pmb_args (Some xbody) xmty ~attrs )
44014412

44024413
let fmt_toplevel_directive c ~semisemi dir =
44034414
let fmt_dir_arg = function

0 commit comments

Comments
 (0)