Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/private-attr-doc-comment-type.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Accept attributes and doc comments after `= private`, and a doc comment after `=` in front of a type expression such as `int`.
95 changes: 57 additions & 38 deletions compiler/syntax/src/res_core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5799,6 +5799,11 @@ and parse_private_eq_or_repr p =
| Bar | DotDot ->
let _, kind, attrs = parse_type_representation p in
(None, Asttypes.Private, kind, attrs)
| (At | DocComment _) when is_representation_after_attrs p ->
let _, kind, attrs = parse_type_representation p in
(None, Asttypes.Private, kind, attrs)
| DocComment _ ->
(Some (parse_typ_expr p), Asttypes.Private, Parsetree.Ptype_abstract, [])
| t when Grammar.is_typ_expr_start t ->
(Some (parse_typ_expr p), Asttypes.Private, Parsetree.Ptype_abstract, [])
| _ ->
Expand Down Expand Up @@ -6019,6 +6024,55 @@ and parse_polymorphic_variant_type_args p =
Parser.expect Rparen p;
Location.mkloc args (mk_loc start_pos (Parser.position p))

(* Attributes and doc comments after `=` (or `= private`) can start a
representation (variant/record/open variant) or a manifest. Look ahead past
them: if a representation-like token follows, it is a representation;
otherwise a manifest type expression. *)
and is_representation_after_attrs p =
let starts_with_doc_comment =
match Parser.peek p with
| DocComment _ -> true
| _ -> false
in
Parser.lookahead p (fun state ->
ignore (parse_attributes state);
(* optionally skip a run of doc comments before deciding *)
skip_doc_comments state;
match Parser.peek state with
| Lbrace -> (
(* Disambiguate record declaration vs object type.
Peek inside the braces; if it looks like an object (String/Dot/DotDot/DotDotDot),
then this is a manifest type expression, not a representation. If it looks like
a record field (e.g. Lident or attributes before one), treat as representation. *)
Parser.next state;
(* consume Lbrace *)
ignore (parse_attributes state);
skip_doc_comments state;
match Parser.peek state with
| String _ | Dot | DotDot -> false (* object type => manifest *)
| DotDotDot ->
(* A spread (`{...base, y: int}`) after a leading doc comment has
always been parsed as a record declaration, and after a leading
attribute as an object type. *)
starts_with_doc_comment
| _ -> true
(* record decl => representation *))
| Bar -> true (* variant constructor list *)
| DotDot -> true (* extensible/open variant ".." *)
| DotDotDot ->
(* A variant spread (`...base | C`) after a leading doc comment has
always been parsed as a constructor list. *)
starts_with_doc_comment
| Uident _ -> (
(* constructor vs module-qualified manifest *)
Parser.next state;
match Parser.peek state with
| Dot -> false (* M.t => manifest *)
| _ -> true
(* Uident starting a constructor *))
| DocComment _ -> true (* doc before constructor list *)
| _ -> false)
Comment thread
cknitt marked this conversation as resolved.

and parse_type_equation_and_representation ?current_type_name_path
?inline_types_context p =
match Parser.peek p with
Expand All @@ -6034,49 +6088,14 @@ and parse_type_equation_and_representation ?current_type_name_path
in
(manifest, priv, kind, [])
| Private -> parse_private_eq_or_repr p
| Bar | DotDot | DocComment _ ->
| Bar | DotDot ->
let priv, kind, attrs =
parse_type_representation ?current_type_name_path ?inline_types_context
p
in
(None, priv, kind, attrs)
| At -> (
(* Attributes can start a representation (variant/record/open variant) or a manifest.
Look ahead past attributes (and doc comments). If a representation-like token follows,
parse it as a representation; otherwise treat as a manifest. *)
let is_representation_after_attrs =
Parser.lookahead p (fun state ->
ignore (parse_attributes state);
(* optionally skip a run of doc comments before deciding *)
skip_doc_comments state;
match Parser.peek state with
| Lbrace -> (
(* Disambiguate record declaration vs object type.
Peek inside the braces; if it looks like an object (String/Dot/DotDot/DotDotDot),
then this is a manifest type expression, not a representation. If it looks like
a record field (e.g. Lident or attributes before one), treat as representation. *)
Parser.next state;
(* consume Lbrace *)
ignore (parse_attributes state);
skip_doc_comments state;
match Parser.peek state with
| String _ | Dot | DotDot | DotDotDot ->
false (* object type => manifest *)
| _ -> true
(* record decl => representation *))
| Bar -> true (* variant constructor list *)
| DotDot -> true (* extensible/open variant ".." *)
| Uident _ -> (
(* constructor vs module-qualified manifest *)
Parser.next state;
match Parser.peek state with
| Dot -> false (* M.t => manifest *)
| _ -> true
(* Uident starting a constructor *))
| DocComment _ -> true (* doc before constructor list *)
| _ -> false)
in
if is_representation_after_attrs then
| At | DocComment _ -> (
if is_representation_after_attrs p then
let priv, kind, attrs =
parse_type_representation ?current_type_name_path
?inline_types_context p
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,73 @@ type nonrec t =
| B [@res.doc {js|doc2|js}]
type nonrec t =
| A [@res.doc {js|doc|js}][@attr ]
| B
| B
type nonrec t = private {
x: int }[@@attr ]
type nonrec t = private ..[@@attr ]
type nonrec t = private
| A [@attr ]
| B
type nonrec t = private ((int)[@attr ])
type nonrec t = private ((< a: int > )[@attr ])
type nonrec t = private ((int)[@res.doc {js|doc|js}])
type nonrec t = private ((M.t)[@res.doc {js|doc|js}])
type nonrec t = private
| A [@res.doc {js|doc|js}]
| B
type nonrec t = private {
x: int }[@@res.doc {js|doc|js}][@@attr ]
type nonrec t = ((int)[@res.doc {js|doc|js}])
type nonrec t = ((< a: int > )[@res.doc {js|doc|js}])
type nonrec t = (((int * int))[@res.doc {js|doc|js}])
type nonrec t = (([ `A ])[@res.doc {js|doc|js}])
type nonrec t = ((M.t)[@res.doc {js|doc|js}])
type nonrec t = ((M.t)[@res.doc {js|doc|js}]) =
| A
| B
type nonrec t = ((int)[@res.doc {js|doc|js}][@attr ])
type nonrec t = {
...: base ;
y: int }[@@res.doc {js|doc|js}]
type nonrec t = {
...: base }[@@res.doc {js|doc|js}]
type nonrec t = {
...: base ;
y: int }[@@res.doc {js|doc|js}][@@attr ]
type nonrec t = ((< .. > )[@res.doc {js|doc|js}])
type nonrec t = ((< > )[@res.doc {js|doc|js}])
type nonrec t = ((< a: int ;.. > )[@res.doc {js|doc|js}])
type nonrec t = ((< base ;y: int > )[@attr ])
type nonrec t = ((< base > )[@attr ])
type nonrec t = private {
...: base ;
y: int }[@@res.doc {js|doc|js}]
type nonrec t = private {
...: base }[@@res.doc {js|doc|js}]
type nonrec t = private {
...: base ;
y: int }[@@res.doc {js|doc|js}][@@attr ]
type nonrec t = private ((< .. > )[@res.doc {js|doc|js}])
type nonrec t = private ((< a: int ;.. > )[@res.doc {js|doc|js}])
type nonrec t = private ((< base ;y: int > )[@attr ])
type nonrec t = private ((< base > )[@attr ])
type nonrec t =
| ... of base [@res.doc {js|doc|js}]
| C
type nonrec t =
| ... of base [@res.doc {js|doc|js}][@res.doc {js|doc2|js}]
| C
type nonrec t =
| ... of base [@res.doc {js|doc|js}][@attr ]
| C
type nonrec t = private
| ... of base [@res.doc {js|doc|js}]
| C
type nonrec t = private
| ... of base [@res.doc {js|doc|js}][@attr ]
| C
type nonrec t = (('a)[@res.doc {js|doc|js}])
type nonrec t = ((int)[@res.doc {js|doc|js}]) -> int (a:1)
type nonrec t = ((M.t)[@res.doc {js|doc|js}]) = {
x: int }
type nonrec t = private (('a Dict.t)[@res.doc {js|doc|js}])
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,45 @@ type t = private /**doc*/ {x: int}
type t = /**doc*/ A | B
type t = /**doc*/ | A | /**doc2*/ B
type t = /**doc*/ @attr A | B

// attributes and doc comments after `private`, and doc comments before a manifest
type t = private @attr {x: int}
type t = private @attr ..
type t = private @attr A | B
type t = private @attr int
type t = private @attr {"a": int}
type t = private /**doc*/ int
type t = private /**doc*/ M.t
type t = private /**doc*/ A | B
type t = private /**doc*/ @attr {x: int}
type t = /**doc*/ int
type t = /**doc*/ {"a": int}
type t = /**doc*/ (int, int)
type t = /**doc*/ [#A]
type t = /**doc*/ M.t
type t = /**doc*/ M.t = A | B
type t = /**doc*/ @attr int
type t = /**doc*/ {...base, y: int}
type t = /**doc*/ {...base}
type t = /**doc*/ @attr {...base, y: int}
type t = /**doc*/ {..}
type t = /**doc*/ {.}
type t = /**doc*/ {.. "a": int}
type t = @attr {...base, "y": int}
type t = @attr {...base}
type t = private /**doc*/ {...base, y: int}
type t = private /**doc*/ {...base}
type t = private /**doc*/ @attr {...base, y: int}
type t = private /**doc*/ {..}
type t = private /**doc*/ {.. "a": int}
type t = private @attr {...base, "y": int}
type t = private @attr {...base}
type t = /**doc*/ ...base | C
type t = /**doc*/ /**doc2*/ ...base | C
type t = /**doc*/ @attr ...base | C
type t = private /**doc*/ ...base | C
type t = private /**doc*/ @attr ...base | C
type t = /**doc*/ 'a
type t = /**doc*/ int => int
type t = /**doc*/ M.t = {x: int}
type t = private /**doc*/ Dict.t<'a>
17 changes: 17 additions & 0 deletions tests/syntax_tests/data/printer/typeDef/attributes.res
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,20 @@ type t = @attr /** doc */ @attr2 ..
type t = /* c1 */ /** doc */ /* c2 */ {x: int} // trail
type t = /** doc */ A | B
type t = | /** doc */ A | /** doc2 */ B
type t = private @attr {x: int}
type t = private /** doc */ @attr ..
type t = private @attr int
type t = private /** doc */ int
type t = private /** doc */ A | B
type t = /** doc */ int
type t = /** doc */ {"a": int}
type t = /** doc */ (int, int)
type t = /** doc */ [#A]
type t = /** doc */ M.t = A | B
type t = /** doc */ {...base, y: int}
type t = /** doc */ {.. "a": int}
type t = private /** doc */ {...base, y: int}
type t = private /** doc */ {.}
type t = /** doc */ ...base | C
type t = private /** doc */ ...base | C
type t = /** doc */ 'a
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,34 @@ type t =
| A
/** doc2 */
| B
@attr type t = private {x: int}
/** doc */
@attr type t = private ..
type t = private @attr int
type t = private /** doc */ int
type t =
private
/** doc */
| A
| B
type t = /** doc */ int
type t = /** doc */ {"a": int}
type t = /** doc */ (int, int)
type t = /** doc */ [#A]
type t = /** doc */ M.t = A | B
/** doc */
type t = {...base, y: int}
type t = /** doc */ {.."a": int}
/** doc */
type t = private {...base, y: int}
type t = private /** doc */ {.}
type t =
/** doc */
| ...base
| C
type t =
private
/** doc */
| ...base
| C
type t = /** doc */ 'a
Loading