Skip to content

Commit 18ff979

Browse files
authored
Parser leading attr strictness (#7787)
* better less strict handling of leading attrs * format * changelog * unify
1 parent 6ea8db7 commit 18ff979

File tree

6 files changed

+89
-11
lines changed

6 files changed

+89
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#### :nail_care: Polish
2828

29+
- Make parser less strict around leading attributes. https://github.com/rescript-lang/rescript/pull/7787
30+
2931
#### :house: Internal
3032

3133
- Build runtime with rewatch on Windows, too. https://github.com/rescript-lang/rescript/pull/7794

compiler/syntax/src/res_core.ml

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ module Parser = Res_parser
1010
let mk_loc start_loc end_loc =
1111
Location.{loc_start = start_loc; loc_end = end_loc; loc_ghost = false}
1212

13+
let rec skip_doc_comments p =
14+
match p.Parser.token with
15+
| DocComment _ ->
16+
Parser.next p;
17+
skip_doc_comments p
18+
| _ -> ()
19+
1320
type inline_types_context = {
1421
mutable found_inline_types:
1522
(string * Warnings.loc * Parsetree.type_kind) list;
@@ -5165,8 +5172,37 @@ and parse_type_representation ?current_type_name_path ?inline_types_context p =
51655172
in
51665173
let kind =
51675174
match p.Parser.token with
5168-
| Bar | Uident _ | DocComment _ | At ->
5175+
| Bar | Uident _ | DocComment _ ->
51695176
Parsetree.Ptype_variant (parse_type_constructor_declarations p)
5177+
| At -> (
5178+
(* Attributes can prefix either a variant (constructor list), a record, or an
5179+
open/extensible variant marker (`..`). Peek past attributes and any doc
5180+
comments to decide which kind it is. *)
5181+
let after_attrs =
5182+
Parser.lookahead p (fun state ->
5183+
ignore (parse_attributes state);
5184+
skip_doc_comments state;
5185+
state.Parser.token)
5186+
in
5187+
match after_attrs with
5188+
| Lbrace ->
5189+
(* consume the attributes and any doc comments before the record *)
5190+
ignore (parse_attributes p);
5191+
skip_doc_comments p;
5192+
Parsetree.Ptype_record
5193+
(parse_record_declaration ?current_type_name_path
5194+
?inline_types_context p)
5195+
| DotDot ->
5196+
(* attributes before an open variant marker; consume attrs/docs then handle `..` *)
5197+
ignore (parse_attributes p);
5198+
skip_doc_comments p;
5199+
Parser.next p;
5200+
(* consume DotDot *)
5201+
Ptype_open
5202+
| _ ->
5203+
(* fall back to variant constructor declarations; leave attributes for the
5204+
constructor parsing so they attach to the first constructor. *)
5205+
Parsetree.Ptype_variant (parse_type_constructor_declarations p))
51705206
| Lbrace ->
51715207
Parsetree.Ptype_record
51725208
(parse_record_declaration ?current_type_name_path ?inline_types_context
@@ -5727,22 +5763,42 @@ and parse_type_equation_and_representation ?current_type_name_path
57275763
let priv, kind = parse_type_representation p in
57285764
(None, priv, kind)
57295765
| 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 =
5766+
(* Attributes can start a representation (variant/record/open variant) or a manifest.
5767+
Look ahead past attributes (and doc comments). If a representation-like token follows,
5768+
parse it as a representation; otherwise treat as a manifest. *)
5769+
let is_representation_after_attrs =
57345770
Parser.lookahead p (fun state ->
57355771
ignore (parse_attributes state);
5772+
(* optionally skip a run of doc comments before deciding *)
5773+
skip_doc_comments state;
57365774
match state.Parser.token with
5775+
| Lbrace -> (
5776+
(* Disambiguate record declaration vs object type.
5777+
Peek inside the braces; if it looks like an object (String/Dot/DotDot/DotDotDot),
5778+
then this is a manifest type expression, not a representation. If it looks like
5779+
a record field (e.g. Lident or attributes before one), treat as representation. *)
5780+
Parser.next state;
5781+
(* consume Lbrace *)
5782+
ignore (parse_attributes state);
5783+
skip_doc_comments state;
5784+
match state.Parser.token with
5785+
| String _ | Dot | DotDot | DotDotDot ->
5786+
false (* object type => manifest *)
5787+
| _ -> true
5788+
(* record decl => representation *))
5789+
| Bar -> true (* variant constructor list *)
5790+
| DotDot -> true (* extensible/open variant ".." *)
57375791
| Uident _ -> (
5792+
(* constructor vs module-qualified manifest *)
57385793
Parser.next state;
57395794
match state.Parser.token with
5740-
| Dot -> false
5741-
| _ -> true)
5742-
| DotDotDot | Bar | DocComment _ -> true
5795+
| Dot -> false (* M.t => manifest *)
5796+
| _ -> true
5797+
(* Uident starting a constructor *))
5798+
| DocComment _ -> true (* doc before constructor list *)
57435799
| _ -> false)
57445800
in
5745-
if is_variant_after_attrs then
5801+
if is_representation_after_attrs then
57465802
let priv, kind = parse_type_representation p in
57475803
(None, priv, kind)
57485804
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)