Skip to content

Commit 123a085

Browse files
committed
better less strict handling of leading attrs
1 parent 93db5f0 commit 123a085

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
@@ -5050,8 +5050,50 @@ and parse_type_representation ?current_type_name_path ?inline_types_context p =
50505050
in
50515051
let kind =
50525052
match p.Parser.token with
5053-
| Bar | Uident _ | DocComment _ | At ->
5053+
| Bar | Uident _ | DocComment _ ->
50545054
Parsetree.Ptype_variant (parse_type_constructor_declarations p)
5055+
| At ->
5056+
(* Attributes can prefix either a variant (constructor list), a record, or an
5057+
open/extensible variant marker (`..`). Peek past attributes and any doc
5058+
comments to decide which kind it is. *)
5059+
let after_attrs =
5060+
Parser.lookahead p (fun state ->
5061+
ignore (parse_attributes state);
5062+
let rec skip_docs () =
5063+
match state.Parser.token with
5064+
| DocComment _ -> Parser.next state; skip_docs ()
5065+
| _ -> ()
5066+
in
5067+
skip_docs ();
5068+
state.Parser.token)
5069+
in
5070+
(match after_attrs with
5071+
| Lbrace ->
5072+
(* consume the attributes and any doc comments before the record *)
5073+
ignore (parse_attributes p);
5074+
let rec skip_docs () =
5075+
match p.Parser.token with
5076+
| DocComment _ -> Parser.next p; skip_docs ()
5077+
| _ -> ()
5078+
in
5079+
skip_docs ();
5080+
Parsetree.Ptype_record
5081+
(parse_record_declaration ?current_type_name_path ?inline_types_context p)
5082+
| DotDot ->
5083+
(* attributes before an open variant marker; consume attrs/docs then handle `..` *)
5084+
ignore (parse_attributes p);
5085+
let rec skip_docs () =
5086+
match p.Parser.token with
5087+
| DocComment _ -> Parser.next p; skip_docs ()
5088+
| _ -> ()
5089+
in
5090+
skip_docs ();
5091+
Parser.next p; (* consume DotDot *)
5092+
Ptype_open
5093+
| _ ->
5094+
(* fall back to variant constructor declarations; leave attributes for the
5095+
constructor parsing so they attach to the first constructor. *)
5096+
Parsetree.Ptype_variant (parse_type_constructor_declarations p))
50555097
| Lbrace ->
50565098
Parsetree.Ptype_record
50575099
(parse_record_declaration ?current_type_name_path ?inline_types_context
@@ -5612,22 +5654,42 @@ and parse_type_equation_and_representation ?current_type_name_path
56125654
let priv, kind = parse_type_representation p in
56135655
(None, priv, kind)
56145656
| At -> (
5615-
(* Attribute can start a variant constructor or a type manifest.
5616-
Look ahead past attributes; if a constructor-like token follows (Uident not immediately
5617-
followed by a Dot, or DotDotDot/Bar/DocComment), treat as variant; otherwise manifest *)
5618-
let is_variant_after_attrs =
5657+
(* Attributes can start a representation (variant/record/open variant) or a manifest.
5658+
Look ahead past attributes (and doc comments). If a representation-like token follows,
5659+
parse it as a representation; otherwise treat as a manifest. *)
5660+
let is_representation_after_attrs =
56195661
Parser.lookahead p (fun state ->
56205662
ignore (parse_attributes state);
5663+
(* optionally skip a run of doc comments before deciding *)
5664+
let rec skip_docs () =
5665+
match state.Parser.token with
5666+
| DocComment _ -> Parser.next state; skip_docs ()
5667+
| _ -> ()
5668+
in
5669+
skip_docs ();
56215670
match state.Parser.token with
5622-
| Uident _ -> (
5671+
| Lbrace ->
5672+
(* Disambiguate record declaration vs object type.
5673+
Peek inside the braces; if it looks like an object (String/Dot/DotDot/DotDotDot),
5674+
then this is a manifest type expression, not a representation. If it looks like
5675+
a record field (e.g. Lident or attributes before one), treat as representation. *)
5676+
Parser.next state; (* consume Lbrace *)
5677+
ignore (parse_attributes state);
5678+
skip_docs ();
5679+
(match state.Parser.token with
5680+
| String _ | Dot | DotDot | DotDotDot -> false (* object type => manifest *)
5681+
| _ -> true) (* record decl => representation *)
5682+
| Bar -> true (* variant constructor list *)
5683+
| DotDot -> true (* extensible/open variant ".." *)
5684+
| Uident _ -> (* constructor vs module-qualified manifest *)
56235685
Parser.next state;
5624-
match state.Parser.token with
5625-
| Dot -> false
5626-
| _ -> true)
5627-
| DotDotDot | Bar | DocComment _ -> true
5686+
(match state.Parser.token with
5687+
| Dot -> false (* M.t => manifest *)
5688+
| _ -> true) (* Uident starting a constructor *)
5689+
| DocComment _ -> true (* doc before constructor list *)
56285690
| _ -> false)
56295691
in
5630-
if is_variant_after_attrs then
5692+
if is_representation_after_attrs then
56315693
let priv, kind = parse_type_representation p in
56325694
(None, priv, kind)
56335695
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)