Skip to content

Commit a2feefb

Browse files
committed
better less strict handling of leading attrs
1 parent 6ea8db7 commit a2feefb

File tree

5 files changed

+95
-13
lines changed

5 files changed

+95
-13
lines changed

compiler/syntax/src/res_core.ml

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5165,8 +5165,50 @@ and parse_type_representation ?current_type_name_path ?inline_types_context p =
51655165
in
51665166
let kind =
51675167
match p.Parser.token with
5168-
| Bar | Uident _ | DocComment _ | At ->
5168+
| Bar | Uident _ | DocComment _ ->
51695169
Parsetree.Ptype_variant (parse_type_constructor_declarations p)
5170+
| At ->
5171+
(* Attributes can prefix either a variant (constructor list), a record, or an
5172+
open/extensible variant marker (`..`). Peek past attributes and any doc
5173+
comments to decide which kind it is. *)
5174+
let after_attrs =
5175+
Parser.lookahead p (fun state ->
5176+
ignore (parse_attributes state);
5177+
let rec skip_docs () =
5178+
match state.Parser.token with
5179+
| DocComment _ -> Parser.next state; skip_docs ()
5180+
| _ -> ()
5181+
in
5182+
skip_docs ();
5183+
state.Parser.token)
5184+
in
5185+
(match after_attrs with
5186+
| Lbrace ->
5187+
(* consume the attributes and any doc comments before the record *)
5188+
ignore (parse_attributes p);
5189+
let rec skip_docs () =
5190+
match p.Parser.token with
5191+
| DocComment _ -> Parser.next p; skip_docs ()
5192+
| _ -> ()
5193+
in
5194+
skip_docs ();
5195+
Parsetree.Ptype_record
5196+
(parse_record_declaration ?current_type_name_path ?inline_types_context p)
5197+
| DotDot ->
5198+
(* attributes before an open variant marker; consume attrs/docs then handle `..` *)
5199+
ignore (parse_attributes p);
5200+
let rec skip_docs () =
5201+
match p.Parser.token with
5202+
| DocComment _ -> Parser.next p; skip_docs ()
5203+
| _ -> ()
5204+
in
5205+
skip_docs ();
5206+
Parser.next p; (* consume DotDot *)
5207+
Ptype_open
5208+
| _ ->
5209+
(* fall back to variant constructor declarations; leave attributes for the
5210+
constructor parsing so they attach to the first constructor. *)
5211+
Parsetree.Ptype_variant (parse_type_constructor_declarations p))
51705212
| Lbrace ->
51715213
Parsetree.Ptype_record
51725214
(parse_record_declaration ?current_type_name_path ?inline_types_context
@@ -5727,22 +5769,42 @@ and parse_type_equation_and_representation ?current_type_name_path
57275769
let priv, kind = parse_type_representation p in
57285770
(None, priv, kind)
57295771
| At -> (
5730-
(* Attribute can start a variant constructor or a type manifest.
5731-
Look ahead past attributes; if a constructor-like token follows (Uident not immediately
5732-
followed by a Dot, or DotDotDot/Bar/DocComment), treat as variant; otherwise manifest *)
5733-
let is_variant_after_attrs =
5772+
(* Attributes can start a representation (variant/record/open variant) or a manifest.
5773+
Look ahead past attributes (and doc comments). If a representation-like token follows,
5774+
parse it as a representation; otherwise treat as a manifest. *)
5775+
let is_representation_after_attrs =
57345776
Parser.lookahead p (fun state ->
57355777
ignore (parse_attributes state);
5778+
(* optionally skip a run of doc comments before deciding *)
5779+
let rec skip_docs () =
5780+
match state.Parser.token with
5781+
| DocComment _ -> Parser.next state; skip_docs ()
5782+
| _ -> ()
5783+
in
5784+
skip_docs ();
57365785
match state.Parser.token with
5737-
| Uident _ -> (
5786+
| Lbrace ->
5787+
(* Disambiguate record declaration vs object type.
5788+
Peek inside the braces; if it looks like an object (String/Dot/DotDot/DotDotDot),
5789+
then this is a manifest type expression, not a representation. If it looks like
5790+
a record field (e.g. Lident or attributes before one), treat as representation. *)
5791+
Parser.next state; (* consume Lbrace *)
5792+
ignore (parse_attributes state);
5793+
skip_docs ();
5794+
(match state.Parser.token with
5795+
| String _ | Dot | DotDot | DotDotDot -> false (* object type => manifest *)
5796+
| _ -> true) (* record decl => representation *)
5797+
| Bar -> true (* variant constructor list *)
5798+
| DotDot -> true (* extensible/open variant ".." *)
5799+
| Uident _ -> (* constructor vs module-qualified manifest *)
57385800
Parser.next state;
5739-
match state.Parser.token with
5740-
| Dot -> false
5741-
| _ -> true)
5742-
| DotDotDot | Bar | DocComment _ -> true
5801+
(match state.Parser.token with
5802+
| Dot -> false (* M.t => manifest *)
5803+
| _ -> true) (* Uident starting a constructor *)
5804+
| DocComment _ -> true (* doc before constructor list *)
57435805
| _ -> false)
57445806
in
5745-
if is_variant_after_attrs then
5807+
if is_representation_after_attrs then
57465808
let priv, kind = parse_type_representation p in
57475809
(None, priv, kind)
57485810
else

tests/syntax_tests/data/parsing/grammar/typedefinition/expected/recordDeclaration.res.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ type nonrec domProps =
5353
{
5454
label: string [@optional ];
5555
list: string [@optional ];
56-
loop: bool [@optional ]}
56+
loop: bool [@optional ]}
57+
type nonrec t = {
58+
x: int }
59+
type nonrec t = {
60+
x: int }

tests/syntax_tests/data/parsing/grammar/typedefinition/expected/typeRepresentation.res.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,10 @@ type nonrec t = {
4141
y: int }
4242
type nonrec t = private {
4343
x: int ;
44-
y: int }
44+
y: int }
45+
type nonrec t = {
46+
x: int }
47+
type nonrec t = {
48+
x: int }
49+
type nonrec t = ..
50+
type nonrec t = ..

tests/syntax_tests/data/parsing/grammar/typedefinition/recordDeclaration.res

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@ type domProps = {
4646
@optional
4747
loop: bool,
4848
}
49+
50+
// attribute allowed before record representation
51+
type t = @attr {x: int}
52+
type t = @attr /**doc before record*/ {x: int}

tests/syntax_tests/data/parsing/grammar/typedefinition/typeRepresentation.res

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ type t = private Blue | Red | Green
2626
type t = {x: int, y: int}
2727
// private record declaration
2828
type t = private {x: int, y: int}
29+
30+
// attributes can precede record or open variant markers
31+
type t = @attr {x: int}
32+
type t = @attr /**doc before record*/ {x: int}
33+
type t = @attr ..
34+
type t = @attr /**doc before open*/ ..

0 commit comments

Comments
 (0)