From 264a90b29eb790dce7d3997db2ce25c86d9e9c90 Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Mon, 7 Nov 2022 18:06:22 +0900 Subject: [PATCH 1/7] parse attributes for function and arg respectively --- res_syntax/src/res_core.ml | 30 +++++++------ res_syntax/src/res_parsetree_viewer.ml | 42 +++++-------------- res_syntax/src/res_printer.ml | 29 +++++++++---- .../expressions/expected/arrow.res.txt | 17 ++++---- .../react/expected/fileLevelConfig.res.txt | 3 +- .../ppx/react/expected/forwardRef.res.txt | 37 ++++++++-------- .../ppx/react/expected/innerModule.res.txt | 22 ++++++---- .../tests/ppx/react/expected/topLevel.res.txt | 11 +++-- .../printer/expr/expected/asyncAwait.res.txt | 6 +-- 9 files changed, 103 insertions(+), 94 deletions(-) diff --git a/res_syntax/src/res_core.ml b/res_syntax/src/res_core.ml index e4048594f2..0c34c9f053 100644 --- a/res_syntax/src/res_core.ml +++ b/res_syntax/src/res_core.ml @@ -1458,6 +1458,12 @@ and parseTernaryExpr leftOperand p = and parseEs6ArrowExpression ?context ?parameters p = let startPos = p.Parser.startPos in Parser.leaveBreadcrumb p Grammar.Es6ArrowExpr; + (* Parsing function parameters and attributes: + 1. Basically, attributes outside of `(...)` are added to the function, except + the uncurried attribute `(.)` is added to the function. e.g. async, uncurried + + 2. Attributes inside `(...)` are added to the arguments regardless of whether + labeled, optional or nolabeled *) let parameters = match parameters with | Some params -> params @@ -1531,12 +1537,6 @@ and parseParameter p = then let startPos = p.Parser.startPos in let uncurried = Parser.optional p Token.Dot in - (* two scenarios: - * attrs ~lbl ... - * attrs pattern - * Attributes before a labelled arg, indicate that it's on the whole arrow expr - * Otherwise it's part of the pattern - * *) let attrs = parseAttributes p in if p.Parser.token = Typ then ( Parser.next p; @@ -1554,9 +1554,9 @@ and parseParameter p = match p.Parser.token with | Comma | Equal | Rparen -> let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, - Ast_helper.Pat.var ~attrs:[propLocAttr] ~loc + Ast_helper.Pat.var ~attrs:(propLocAttr :: attrs) ~loc (Location.mkloc lblName loc) ) | Colon -> let lblEnd = p.prevEndPos in @@ -1566,25 +1566,29 @@ and parseParameter p = let pat = let pat = Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) in let loc = mkLoc startPos p.prevEndPos in - Ast_helper.Pat.constraint_ ~attrs:[propLocAttr] ~loc pat typ + Ast_helper.Pat.constraint_ ~attrs:(propLocAttr :: attrs) ~loc pat + typ in (attrs, Asttypes.Labelled lblName, pat) | As -> Parser.next p; let pat = let pat = parseConstrainedPattern p in - {pat with ppat_attributes = propLocAttr :: pat.ppat_attributes} + { + pat with + ppat_attributes = (propLocAttr :: attrs) @ pat.ppat_attributes; + } in - (attrs, Asttypes.Labelled lblName, pat) + ([], Asttypes.Labelled lblName, pat) | t -> Parser.err p (Diagnostics.unexpected t p.breadcrumbs); let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) )) | _ -> let pattern = parseConstrainedPattern p in - let attrs = List.concat [attrs; pattern.ppat_attributes] in + let attrs = List.concat [pattern.ppat_attributes; attrs] in ([], Asttypes.Nolabel, {pattern with ppat_attributes = attrs}) in match p.Parser.token with diff --git a/res_syntax/src/res_parsetree_viewer.ml b/res_syntax/src/res_parsetree_viewer.ml index 7ab2a37303..be0cd813e7 100644 --- a/res_syntax/src/res_parsetree_viewer.ml +++ b/res_syntax/src/res_parsetree_viewer.ml @@ -136,7 +136,7 @@ let funExpr expr = collectNewTypes (stringLoc :: acc) returnExpr | returnExpr -> (List.rev acc, returnExpr) in - let rec collect n attrsBefore acc expr = + let rec collect attrsBefore acc expr = match expr with | { pexp_desc = @@ -147,48 +147,26 @@ let funExpr expr = {pexp_desc = Pexp_apply _} ); } -> (attrsBefore, List.rev acc, rewriteUnderscoreApply expr) - | { - pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); - pexp_attributes = []; - } -> - let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in - collect (n + 1) attrsBefore (parameter :: acc) returnExpr | {pexp_desc = Pexp_newtype (stringLoc, rest); pexp_attributes = attrs} -> let stringLocs, returnExpr = collectNewTypes [stringLoc] rest in let param = NewTypes {attrs; locs = stringLocs} in - collect (n + 1) attrsBefore (param :: acc) returnExpr - | {pexp_desc = Pexp_fun _; pexp_attributes} - when pexp_attributes - |> List.exists (fun ({Location.txt}, _) -> - txt = "bs" || txt = "res.async") - && n > 0 -> - (* stop here, the uncurried or async attribute always indicates the beginning of an arrow function - * e.g. `(. a) => (. b)` instead of `(. a, . b)` *) - (attrsBefore, List.rev acc, expr) + collect attrsBefore (param :: acc) returnExpr | { - pexp_desc = - Pexp_fun - (((Labelled _ | Optional _) as lbl), defaultExpr, pattern, returnExpr); - pexp_attributes = attrs; + pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); + pexp_attributes = []; } -> - (* Normally attributes are attached to the labelled argument, e.g. (@foo ~x) => ... - In the case of `@res.async`, pass the attribute to the outside *) - let attrs_async, attrs_other = - attrs |> List.partition (fun ({Location.txt}, _) -> txt = "res.async") - in - let parameter = - Parameter {attrs = attrs_other; lbl; defaultExpr; pat = pattern} - in - collect (n + 1) (attrs_async @ attrsBefore) (parameter :: acc) returnExpr + let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in + collect attrsBefore (parameter :: acc) returnExpr + | {pexp_desc = Pexp_fun _} -> (attrsBefore, List.rev acc, expr) | expr -> (attrsBefore, List.rev acc, expr) in match expr with | { - pexp_desc = Pexp_fun (Nolabel, _defaultExpr, _pattern, _returnExpr); + pexp_desc = Pexp_fun (_, _defaultExpr, _pattern, _returnExpr); pexp_attributes = attrs; } as expr -> - collect 0 attrs [] {expr with pexp_attributes = []} - | expr -> collect 0 [] [] expr + collect attrs [] {expr with pexp_attributes = []} + | expr -> collect [] [] expr let processBracesAttr expr = match expr.pexp_attributes with diff --git a/res_syntax/src/res_printer.ml b/res_syntax/src/res_printer.ml index 7c4efa2349..3dcd103439 100644 --- a/res_syntax/src/res_printer.ml +++ b/res_syntax/src/res_printer.ml @@ -4835,13 +4835,23 @@ and printExprFunParameters ~customLayout ~inCallback ~async ~uncurried attrs = []; lbl = Asttypes.Nolabel; defaultExpr = None; - pat = {Parsetree.ppat_desc = Ppat_var stringLoc}; + pat = + { + Parsetree.ppat_desc = Ppat_var stringLoc; + Parsetree.ppat_attributes = attrs; + }; }; ] when not uncurried -> let txtDoc = let var = printIdentLike stringLoc.txt in - let var = if hasConstraint then addParens var else var in + let var = + match attrs with + | [] -> if hasConstraint then addParens var else var + | attrs -> + let attrs = printAttributes ~customLayout attrs cmtTbl in + addParens (Doc.concat [attrs; var]) + in if async then addAsync var else var in printComments txtDoc cmtTbl stringLoc.loc @@ -4932,22 +4942,25 @@ and printExpFunParameter ~customLayout parameter cmtTbl = match (lbl, pattern) with | Asttypes.Nolabel, pattern -> printPattern ~customLayout pattern cmtTbl | ( (Asttypes.Labelled lbl | Optional lbl), - { - ppat_desc = Ppat_var stringLoc; - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; - } ) + {ppat_desc = Ppat_var stringLoc; ppat_attributes} ) when lbl = stringLoc.txt -> (* ~d *) - Doc.concat [Doc.text "~"; printIdentLike lbl] + Doc.concat + [ + printAttributes ~customLayout ppat_attributes cmtTbl; + Doc.text "~"; + printIdentLike lbl; + ] | ( (Asttypes.Labelled lbl | Optional lbl), { ppat_desc = Ppat_constraint ({ppat_desc = Ppat_var {txt}}, typ); - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; + ppat_attributes; } ) when lbl = txt -> (* ~d: e *) Doc.concat [ + printAttributes ~customLayout ppat_attributes cmtTbl; Doc.text "~"; printIdentLike lbl; Doc.text ": "; diff --git a/res_syntax/tests/parsing/grammar/expressions/expected/arrow.res.txt b/res_syntax/tests/parsing/grammar/expressions/expected/arrow.res.txt index 5ab739fe5d..9ce7fb3db4 100644 --- a/res_syntax/tests/parsing/grammar/expressions/expected/arrow.res.txt +++ b/res_syntax/tests/parsing/grammar/expressions/expected/arrow.res.txt @@ -46,16 +46,17 @@ let f = ((fun a -> fun b -> fun c -> ())[@bs ]) let f = ((fun a -> fun b -> ((fun c -> fun d -> ())[@bs ]))[@bs ]) let f = ((fun a -> ((fun b -> ((fun c -> ())[@bs ]))[@bs ]))[@bs ]) let f = - ((fun ~a:((a)[@ns.namedArgLoc ]) -> - fun b -> ((fun ~c:((c)[@ns.namedArgLoc ]) -> fun d -> ()) - [@bs ][@attr ])) - [@bs ][@attr ]) + ((fun ~a:((a)[@ns.namedArgLoc ][@attr ]) -> + fun b -> ((fun ~c:((c)[@ns.namedArgLoc ][@attr ]) -> fun d -> ()) + [@bs ])) + [@bs ]) let f = - ((fun ~a:((a)[@ns.namedArgLoc ]) -> + ((fun ~a:((a)[@ns.namedArgLoc ][@attr ]) -> fun ((b)[@attrOnB ]) -> - ((fun ~c:((c)[@ns.namedArgLoc ]) -> fun ((d)[@attrOnD ]) -> ()) - [@bs ][@attr ])) - [@bs ][@attr ]) + ((fun ~c:((c)[@ns.namedArgLoc ][@attr ]) -> + fun ((d)[@attrOnD ]) -> ()) + [@bs ])) + [@bs ]) let f list = list () ;;match colour with | Red when diff --git a/res_syntax/tests/ppx/react/expected/fileLevelConfig.res.txt b/res_syntax/tests/ppx/react/expected/fileLevelConfig.res.txt index b83a88e209..ab0fe23f95 100644 --- a/res_syntax/tests/ppx/react/expected/fileLevelConfig.res.txt +++ b/res_syntax/tests/ppx/react/expected/fileLevelConfig.res.txt @@ -5,7 +5,8 @@ module V3 = { @react.component let make = - (@warning("-16") ~msg) => { + @warning("-16") + (~msg) => { ReactDOMRe.createDOMElementVariadic("div", [{msg->React.string}]) } let make = { diff --git a/res_syntax/tests/ppx/react/expected/forwardRef.res.txt b/res_syntax/tests/ppx/react/expected/forwardRef.res.txt index 73af6f9c1c..45703e4727 100644 --- a/res_syntax/tests/ppx/react/expected/forwardRef.res.txt +++ b/res_syntax/tests/ppx/react/expected/forwardRef.res.txt @@ -13,25 +13,28 @@ module V3 = { @react.component let make = - (@warning("-16") ~className=?, @warning("-16") ~children) => + @warning("-16") + (~className=?) => @warning("-16") - ref => - ReactDOMRe.createDOMElementVariadic( - "div", - [ - ReactDOMRe.createDOMElementVariadic( - "input", - ~props=ReactDOMRe.domProps( - ~type_="text", - ~className?, - ~ref=?{Js.Nullable.toOption(ref)->Belt.Option.map(ReactDOM.Ref.domRef)}, - (), + (~children) => + @warning("-16") + ref => + ReactDOMRe.createDOMElementVariadic( + "div", + [ + ReactDOMRe.createDOMElementVariadic( + "input", + ~props=ReactDOMRe.domProps( + ~type_="text", + ~className?, + ~ref=?{Js.Nullable.toOption(ref)->Belt.Option.map(ReactDOM.Ref.domRef)}, + (), + ), + [], ), - [], - ), - children, - ], - ) + children, + ], + ) let make = React.forwardRef({ let \"ForwardRef$V3$FancyInput" = ( \"Props": {"className": option<'className>, "children": 'children}, diff --git a/res_syntax/tests/ppx/react/expected/innerModule.res.txt b/res_syntax/tests/ppx/react/expected/innerModule.res.txt index 28472764ce..2a0a1cbf67 100644 --- a/res_syntax/tests/ppx/react/expected/innerModule.res.txt +++ b/res_syntax/tests/ppx/react/expected/innerModule.res.txt @@ -4,10 +4,13 @@ module Bar = { @react.component let make = - (@warning("-16") ~a, @warning("-16") ~b, _) => { - Js.log("This function should be named `InnerModule.react$Bar`") - ReactDOMRe.createDOMElementVariadic("div", []) - } + @warning("-16") + (~a) => + @warning("-16") + (~b, _) => { + Js.log("This function should be named `InnerModule.react$Bar`") + ReactDOMRe.createDOMElementVariadic("div", []) + } let make = { let \"InnerModule$Bar" = (\"Props": {"a": 'a, "b": 'b}) => make(~b=\"Props"["b"], ~a=\"Props"["a"], ()) @@ -17,10 +20,13 @@ module Bar = { @react.component let component = - (@warning("-16") ~a, @warning("-16") ~b, _) => { - Js.log("This function should be named `InnerModule.react$Bar$component`") - ReactDOMRe.createDOMElementVariadic("div", []) - } + @warning("-16") + (~a) => + @warning("-16") + (~b, _) => { + Js.log("This function should be named `InnerModule.react$Bar$component`") + ReactDOMRe.createDOMElementVariadic("div", []) + } let component = { let \"InnerModule$Bar$component" = (\"Props": {"a": 'a, "b": 'b}) => component(~b=\"Props"["b"], ~a=\"Props"["a"], ()) diff --git a/res_syntax/tests/ppx/react/expected/topLevel.res.txt b/res_syntax/tests/ppx/react/expected/topLevel.res.txt index aedfcdf669..35138e80da 100644 --- a/res_syntax/tests/ppx/react/expected/topLevel.res.txt +++ b/res_syntax/tests/ppx/react/expected/topLevel.res.txt @@ -5,10 +5,13 @@ module V3 = { @react.component let make = - (@warning("-16") ~a, @warning("-16") ~b, _) => { - Js.log("This function should be named 'TopLevel.react'") - ReactDOMRe.createDOMElementVariadic("div", []) - } + @warning("-16") + (~a) => + @warning("-16") + (~b, _) => { + Js.log("This function should be named 'TopLevel.react'") + ReactDOMRe.createDOMElementVariadic("div", []) + } let make = { let \"TopLevel$V3" = (\"Props": {"a": 'a, "b": 'b}) => make(~b=\"Props"["b"], ~a=\"Props"["a"], ()) diff --git a/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt b/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt index e9f2b82c20..94c0d12054 100644 --- a/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt +++ b/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt @@ -106,7 +106,7 @@ let _ = await { let f1 = async (~x, ~y) => x + y let f2 = async (@foo ~x, @bar ~y) => x + y -let f3 = async (@bar @foo ~x as @zz z, ~y) => x + y +let f3 = @foo async (~x as @bar @zz z, ~y) => x + y let f4 = async x => x let f5 = async x => async y => 3 let f6 = async (~x1, ~x2) => async y => 3 @@ -116,8 +116,8 @@ let f9 = x => async (~y) => 3 let f10 = x => async y => 3 let f11 = (. ~x) => (. ~y) => 3 -let f12 = @a x => 3 -let f13 = (@a @b ~x) => 3 +let f12 = @a (@b x) => 3 +let f13 = @a @b (~x) => 3 let aw = (await server->start)->foo let aw = @foo (server->start)->foo From dced5b78a7307b346365b074d7ed1955f2a87dcb Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Mon, 7 Nov 2022 18:20:33 +0900 Subject: [PATCH 2/7] add artifacts --- lib/4.06.1/unstable/js_compiler.ml | 71 ++++++------ lib/4.06.1/unstable/js_playground_compiler.ml | 101 +++++++++--------- lib/4.06.1/whole_compiler.ml | 101 +++++++++--------- 3 files changed, 127 insertions(+), 146 deletions(-) diff --git a/lib/4.06.1/unstable/js_compiler.ml b/lib/4.06.1/unstable/js_compiler.ml index 7d99766dd1..c34a3e9b21 100644 --- a/lib/4.06.1/unstable/js_compiler.ml +++ b/lib/4.06.1/unstable/js_compiler.ml @@ -49677,7 +49677,7 @@ let funExpr expr = collectNewTypes (stringLoc :: acc) returnExpr | returnExpr -> (List.rev acc, returnExpr) in - let rec collect n attrsBefore acc expr = + let rec collect attrsBefore acc expr = match expr with | { pexp_desc = @@ -49688,48 +49688,26 @@ let funExpr expr = {pexp_desc = Pexp_apply _} ); } -> (attrsBefore, List.rev acc, rewriteUnderscoreApply expr) - | { - pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); - pexp_attributes = []; - } -> - let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in - collect (n + 1) attrsBefore (parameter :: acc) returnExpr | {pexp_desc = Pexp_newtype (stringLoc, rest); pexp_attributes = attrs} -> let stringLocs, returnExpr = collectNewTypes [stringLoc] rest in let param = NewTypes {attrs; locs = stringLocs} in - collect (n + 1) attrsBefore (param :: acc) returnExpr - | {pexp_desc = Pexp_fun _; pexp_attributes} - when pexp_attributes - |> List.exists (fun ({Location.txt}, _) -> - txt = "bs" || txt = "res.async") - && n > 0 -> - (* stop here, the uncurried or async attribute always indicates the beginning of an arrow function - * e.g. `(. a) => (. b)` instead of `(. a, . b)` *) - (attrsBefore, List.rev acc, expr) + collect attrsBefore (param :: acc) returnExpr | { - pexp_desc = - Pexp_fun - (((Labelled _ | Optional _) as lbl), defaultExpr, pattern, returnExpr); - pexp_attributes = attrs; + pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); + pexp_attributes = []; } -> - (* Normally attributes are attached to the labelled argument, e.g. (@foo ~x) => ... - In the case of `@res.async`, pass the attribute to the outside *) - let attrs_async, attrs_other = - attrs |> List.partition (fun ({Location.txt}, _) -> txt = "res.async") - in - let parameter = - Parameter {attrs = attrs_other; lbl; defaultExpr; pat = pattern} - in - collect (n + 1) (attrs_async @ attrsBefore) (parameter :: acc) returnExpr + let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in + collect attrsBefore (parameter :: acc) returnExpr + | {pexp_desc = Pexp_fun _} -> (attrsBefore, List.rev acc, expr) | expr -> (attrsBefore, List.rev acc, expr) in match expr with | { - pexp_desc = Pexp_fun (Nolabel, _defaultExpr, _pattern, _returnExpr); + pexp_desc = Pexp_fun (_, _defaultExpr, _pattern, _returnExpr); pexp_attributes = attrs; } as expr -> - collect 0 attrs [] {expr with pexp_attributes = []} - | expr -> collect 0 [] [] expr + collect attrs [] {expr with pexp_attributes = []} + | expr -> collect [] [] expr let processBracesAttr expr = match expr.pexp_attributes with @@ -57920,13 +57898,23 @@ and printExprFunParameters ~customLayout ~inCallback ~async ~uncurried attrs = []; lbl = Asttypes.Nolabel; defaultExpr = None; - pat = {Parsetree.ppat_desc = Ppat_var stringLoc}; + pat = + { + Parsetree.ppat_desc = Ppat_var stringLoc; + Parsetree.ppat_attributes = attrs; + }; }; ] when not uncurried -> let txtDoc = let var = printIdentLike stringLoc.txt in - let var = if hasConstraint then addParens var else var in + let var = + match attrs with + | [] -> if hasConstraint then addParens var else var + | attrs -> + let attrs = printAttributes ~customLayout attrs cmtTbl in + addParens (Doc.concat [attrs; var]) + in if async then addAsync var else var in printComments txtDoc cmtTbl stringLoc.loc @@ -58017,22 +58005,25 @@ and printExpFunParameter ~customLayout parameter cmtTbl = match (lbl, pattern) with | Asttypes.Nolabel, pattern -> printPattern ~customLayout pattern cmtTbl | ( (Asttypes.Labelled lbl | Optional lbl), - { - ppat_desc = Ppat_var stringLoc; - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; - } ) + {ppat_desc = Ppat_var stringLoc; ppat_attributes} ) when lbl = stringLoc.txt -> (* ~d *) - Doc.concat [Doc.text "~"; printIdentLike lbl] + Doc.concat + [ + printAttributes ~customLayout ppat_attributes cmtTbl; + Doc.text "~"; + printIdentLike lbl; + ] | ( (Asttypes.Labelled lbl | Optional lbl), { ppat_desc = Ppat_constraint ({ppat_desc = Ppat_var {txt}}, typ); - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; + ppat_attributes; } ) when lbl = txt -> (* ~d: e *) Doc.concat [ + printAttributes ~customLayout ppat_attributes cmtTbl; Doc.text "~"; printIdentLike lbl; Doc.text ": "; diff --git a/lib/4.06.1/unstable/js_playground_compiler.ml b/lib/4.06.1/unstable/js_playground_compiler.ml index 2f34213c33..8f5e29e594 100644 --- a/lib/4.06.1/unstable/js_playground_compiler.ml +++ b/lib/4.06.1/unstable/js_playground_compiler.ml @@ -49677,7 +49677,7 @@ let funExpr expr = collectNewTypes (stringLoc :: acc) returnExpr | returnExpr -> (List.rev acc, returnExpr) in - let rec collect n attrsBefore acc expr = + let rec collect attrsBefore acc expr = match expr with | { pexp_desc = @@ -49688,48 +49688,26 @@ let funExpr expr = {pexp_desc = Pexp_apply _} ); } -> (attrsBefore, List.rev acc, rewriteUnderscoreApply expr) - | { - pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); - pexp_attributes = []; - } -> - let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in - collect (n + 1) attrsBefore (parameter :: acc) returnExpr | {pexp_desc = Pexp_newtype (stringLoc, rest); pexp_attributes = attrs} -> let stringLocs, returnExpr = collectNewTypes [stringLoc] rest in let param = NewTypes {attrs; locs = stringLocs} in - collect (n + 1) attrsBefore (param :: acc) returnExpr - | {pexp_desc = Pexp_fun _; pexp_attributes} - when pexp_attributes - |> List.exists (fun ({Location.txt}, _) -> - txt = "bs" || txt = "res.async") - && n > 0 -> - (* stop here, the uncurried or async attribute always indicates the beginning of an arrow function - * e.g. `(. a) => (. b)` instead of `(. a, . b)` *) - (attrsBefore, List.rev acc, expr) + collect attrsBefore (param :: acc) returnExpr | { - pexp_desc = - Pexp_fun - (((Labelled _ | Optional _) as lbl), defaultExpr, pattern, returnExpr); - pexp_attributes = attrs; + pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); + pexp_attributes = []; } -> - (* Normally attributes are attached to the labelled argument, e.g. (@foo ~x) => ... - In the case of `@res.async`, pass the attribute to the outside *) - let attrs_async, attrs_other = - attrs |> List.partition (fun ({Location.txt}, _) -> txt = "res.async") - in - let parameter = - Parameter {attrs = attrs_other; lbl; defaultExpr; pat = pattern} - in - collect (n + 1) (attrs_async @ attrsBefore) (parameter :: acc) returnExpr + let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in + collect attrsBefore (parameter :: acc) returnExpr + | {pexp_desc = Pexp_fun _} -> (attrsBefore, List.rev acc, expr) | expr -> (attrsBefore, List.rev acc, expr) in match expr with | { - pexp_desc = Pexp_fun (Nolabel, _defaultExpr, _pattern, _returnExpr); + pexp_desc = Pexp_fun (_, _defaultExpr, _pattern, _returnExpr); pexp_attributes = attrs; } as expr -> - collect 0 attrs [] {expr with pexp_attributes = []} - | expr -> collect 0 [] [] expr + collect attrs [] {expr with pexp_attributes = []} + | expr -> collect [] [] expr let processBracesAttr expr = match expr.pexp_attributes with @@ -57920,13 +57898,23 @@ and printExprFunParameters ~customLayout ~inCallback ~async ~uncurried attrs = []; lbl = Asttypes.Nolabel; defaultExpr = None; - pat = {Parsetree.ppat_desc = Ppat_var stringLoc}; + pat = + { + Parsetree.ppat_desc = Ppat_var stringLoc; + Parsetree.ppat_attributes = attrs; + }; }; ] when not uncurried -> let txtDoc = let var = printIdentLike stringLoc.txt in - let var = if hasConstraint then addParens var else var in + let var = + match attrs with + | [] -> if hasConstraint then addParens var else var + | attrs -> + let attrs = printAttributes ~customLayout attrs cmtTbl in + addParens (Doc.concat [attrs; var]) + in if async then addAsync var else var in printComments txtDoc cmtTbl stringLoc.loc @@ -58017,22 +58005,25 @@ and printExpFunParameter ~customLayout parameter cmtTbl = match (lbl, pattern) with | Asttypes.Nolabel, pattern -> printPattern ~customLayout pattern cmtTbl | ( (Asttypes.Labelled lbl | Optional lbl), - { - ppat_desc = Ppat_var stringLoc; - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; - } ) + {ppat_desc = Ppat_var stringLoc; ppat_attributes} ) when lbl = stringLoc.txt -> (* ~d *) - Doc.concat [Doc.text "~"; printIdentLike lbl] + Doc.concat + [ + printAttributes ~customLayout ppat_attributes cmtTbl; + Doc.text "~"; + printIdentLike lbl; + ] | ( (Asttypes.Labelled lbl | Optional lbl), { ppat_desc = Ppat_constraint ({ppat_desc = Ppat_var {txt}}, typ); - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; + ppat_attributes; } ) when lbl = txt -> (* ~d: e *) Doc.concat [ + printAttributes ~customLayout ppat_attributes cmtTbl; Doc.text "~"; printIdentLike lbl; Doc.text ": "; @@ -284712,6 +284703,12 @@ and parseTernaryExpr leftOperand p = and parseEs6ArrowExpression ?context ?parameters p = let startPos = p.Parser.startPos in Parser.leaveBreadcrumb p Grammar.Es6ArrowExpr; + (* Parsing function parameters and attributes: + 1. Basically, attributes outside of `(...)` are added to the function, except + the uncurried attribute `(.)` is added to the function. e.g. async, uncurried + + 2. Attributes inside `(...)` are added to the arguments regardless of whether + labeled, optional or nolabeled *) let parameters = match parameters with | Some params -> params @@ -284785,12 +284782,6 @@ and parseParameter p = then let startPos = p.Parser.startPos in let uncurried = Parser.optional p Token.Dot in - (* two scenarios: - * attrs ~lbl ... - * attrs pattern - * Attributes before a labelled arg, indicate that it's on the whole arrow expr - * Otherwise it's part of the pattern - * *) let attrs = parseAttributes p in if p.Parser.token = Typ then ( Parser.next p; @@ -284808,9 +284799,9 @@ and parseParameter p = match p.Parser.token with | Comma | Equal | Rparen -> let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, - Ast_helper.Pat.var ~attrs:[propLocAttr] ~loc + Ast_helper.Pat.var ~attrs:(propLocAttr :: attrs) ~loc (Location.mkloc lblName loc) ) | Colon -> let lblEnd = p.prevEndPos in @@ -284820,25 +284811,29 @@ and parseParameter p = let pat = let pat = Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) in let loc = mkLoc startPos p.prevEndPos in - Ast_helper.Pat.constraint_ ~attrs:[propLocAttr] ~loc pat typ + Ast_helper.Pat.constraint_ ~attrs:(propLocAttr :: attrs) ~loc pat + typ in (attrs, Asttypes.Labelled lblName, pat) | As -> Parser.next p; let pat = let pat = parseConstrainedPattern p in - {pat with ppat_attributes = propLocAttr :: pat.ppat_attributes} + { + pat with + ppat_attributes = (propLocAttr :: attrs) @ pat.ppat_attributes; + } in - (attrs, Asttypes.Labelled lblName, pat) + ([], Asttypes.Labelled lblName, pat) | t -> Parser.err p (Diagnostics.unexpected t p.breadcrumbs); let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) )) | _ -> let pattern = parseConstrainedPattern p in - let attrs = List.concat [attrs; pattern.ppat_attributes] in + let attrs = List.concat [pattern.ppat_attributes; attrs] in ([], Asttypes.Nolabel, {pattern with ppat_attributes = attrs}) in match p.Parser.token with diff --git a/lib/4.06.1/whole_compiler.ml b/lib/4.06.1/whole_compiler.ml index 63f5989fb3..29664e3e7c 100644 --- a/lib/4.06.1/whole_compiler.ml +++ b/lib/4.06.1/whole_compiler.ml @@ -225539,7 +225539,7 @@ let funExpr expr = collectNewTypes (stringLoc :: acc) returnExpr | returnExpr -> (List.rev acc, returnExpr) in - let rec collect n attrsBefore acc expr = + let rec collect attrsBefore acc expr = match expr with | { pexp_desc = @@ -225550,48 +225550,26 @@ let funExpr expr = {pexp_desc = Pexp_apply _} ); } -> (attrsBefore, List.rev acc, rewriteUnderscoreApply expr) - | { - pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); - pexp_attributes = []; - } -> - let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in - collect (n + 1) attrsBefore (parameter :: acc) returnExpr | {pexp_desc = Pexp_newtype (stringLoc, rest); pexp_attributes = attrs} -> let stringLocs, returnExpr = collectNewTypes [stringLoc] rest in let param = NewTypes {attrs; locs = stringLocs} in - collect (n + 1) attrsBefore (param :: acc) returnExpr - | {pexp_desc = Pexp_fun _; pexp_attributes} - when pexp_attributes - |> List.exists (fun ({Location.txt}, _) -> - txt = "bs" || txt = "res.async") - && n > 0 -> - (* stop here, the uncurried or async attribute always indicates the beginning of an arrow function - * e.g. `(. a) => (. b)` instead of `(. a, . b)` *) - (attrsBefore, List.rev acc, expr) + collect attrsBefore (param :: acc) returnExpr | { - pexp_desc = - Pexp_fun - (((Labelled _ | Optional _) as lbl), defaultExpr, pattern, returnExpr); - pexp_attributes = attrs; + pexp_desc = Pexp_fun (lbl, defaultExpr, pattern, returnExpr); + pexp_attributes = []; } -> - (* Normally attributes are attached to the labelled argument, e.g. (@foo ~x) => ... - In the case of `@res.async`, pass the attribute to the outside *) - let attrs_async, attrs_other = - attrs |> List.partition (fun ({Location.txt}, _) -> txt = "res.async") - in - let parameter = - Parameter {attrs = attrs_other; lbl; defaultExpr; pat = pattern} - in - collect (n + 1) (attrs_async @ attrsBefore) (parameter :: acc) returnExpr + let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in + collect attrsBefore (parameter :: acc) returnExpr + | {pexp_desc = Pexp_fun _} -> (attrsBefore, List.rev acc, expr) | expr -> (attrsBefore, List.rev acc, expr) in match expr with | { - pexp_desc = Pexp_fun (Nolabel, _defaultExpr, _pattern, _returnExpr); + pexp_desc = Pexp_fun (_, _defaultExpr, _pattern, _returnExpr); pexp_attributes = attrs; } as expr -> - collect 0 attrs [] {expr with pexp_attributes = []} - | expr -> collect 0 [] [] expr + collect attrs [] {expr with pexp_attributes = []} + | expr -> collect [] [] expr let processBracesAttr expr = match expr.pexp_attributes with @@ -233782,13 +233760,23 @@ and printExprFunParameters ~customLayout ~inCallback ~async ~uncurried attrs = []; lbl = Asttypes.Nolabel; defaultExpr = None; - pat = {Parsetree.ppat_desc = Ppat_var stringLoc}; + pat = + { + Parsetree.ppat_desc = Ppat_var stringLoc; + Parsetree.ppat_attributes = attrs; + }; }; ] when not uncurried -> let txtDoc = let var = printIdentLike stringLoc.txt in - let var = if hasConstraint then addParens var else var in + let var = + match attrs with + | [] -> if hasConstraint then addParens var else var + | attrs -> + let attrs = printAttributes ~customLayout attrs cmtTbl in + addParens (Doc.concat [attrs; var]) + in if async then addAsync var else var in printComments txtDoc cmtTbl stringLoc.loc @@ -233879,22 +233867,25 @@ and printExpFunParameter ~customLayout parameter cmtTbl = match (lbl, pattern) with | Asttypes.Nolabel, pattern -> printPattern ~customLayout pattern cmtTbl | ( (Asttypes.Labelled lbl | Optional lbl), - { - ppat_desc = Ppat_var stringLoc; - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; - } ) + {ppat_desc = Ppat_var stringLoc; ppat_attributes} ) when lbl = stringLoc.txt -> (* ~d *) - Doc.concat [Doc.text "~"; printIdentLike lbl] + Doc.concat + [ + printAttributes ~customLayout ppat_attributes cmtTbl; + Doc.text "~"; + printIdentLike lbl; + ] | ( (Asttypes.Labelled lbl | Optional lbl), { ppat_desc = Ppat_constraint ({ppat_desc = Ppat_var {txt}}, typ); - ppat_attributes = [] | [({Location.txt = "ns.namedArgLoc"}, _)]; + ppat_attributes; } ) when lbl = txt -> (* ~d: e *) Doc.concat [ + printAttributes ~customLayout ppat_attributes cmtTbl; Doc.text "~"; printIdentLike lbl; Doc.text ": "; @@ -298144,6 +298135,12 @@ and parseTernaryExpr leftOperand p = and parseEs6ArrowExpression ?context ?parameters p = let startPos = p.Parser.startPos in Parser.leaveBreadcrumb p Grammar.Es6ArrowExpr; + (* Parsing function parameters and attributes: + 1. Basically, attributes outside of `(...)` are added to the function, except + the uncurried attribute `(.)` is added to the function. e.g. async, uncurried + + 2. Attributes inside `(...)` are added to the arguments regardless of whether + labeled, optional or nolabeled *) let parameters = match parameters with | Some params -> params @@ -298217,12 +298214,6 @@ and parseParameter p = then let startPos = p.Parser.startPos in let uncurried = Parser.optional p Token.Dot in - (* two scenarios: - * attrs ~lbl ... - * attrs pattern - * Attributes before a labelled arg, indicate that it's on the whole arrow expr - * Otherwise it's part of the pattern - * *) let attrs = parseAttributes p in if p.Parser.token = Typ then ( Parser.next p; @@ -298240,9 +298231,9 @@ and parseParameter p = match p.Parser.token with | Comma | Equal | Rparen -> let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, - Ast_helper.Pat.var ~attrs:[propLocAttr] ~loc + Ast_helper.Pat.var ~attrs:(propLocAttr :: attrs) ~loc (Location.mkloc lblName loc) ) | Colon -> let lblEnd = p.prevEndPos in @@ -298252,25 +298243,29 @@ and parseParameter p = let pat = let pat = Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) in let loc = mkLoc startPos p.prevEndPos in - Ast_helper.Pat.constraint_ ~attrs:[propLocAttr] ~loc pat typ + Ast_helper.Pat.constraint_ ~attrs:(propLocAttr :: attrs) ~loc pat + typ in (attrs, Asttypes.Labelled lblName, pat) | As -> Parser.next p; let pat = let pat = parseConstrainedPattern p in - {pat with ppat_attributes = propLocAttr :: pat.ppat_attributes} + { + pat with + ppat_attributes = (propLocAttr :: attrs) @ pat.ppat_attributes; + } in - (attrs, Asttypes.Labelled lblName, pat) + ([], Asttypes.Labelled lblName, pat) | t -> Parser.err p (Diagnostics.unexpected t p.breadcrumbs); let loc = mkLoc startPos p.prevEndPos in - ( attrs, + ( [], Asttypes.Labelled lblName, Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) )) | _ -> let pattern = parseConstrainedPattern p in - let attrs = List.concat [attrs; pattern.ppat_attributes] in + let attrs = List.concat [pattern.ppat_attributes; attrs] in ([], Asttypes.Nolabel, {pattern with ppat_attributes = attrs}) in match p.Parser.token with From 823a4e3ef3a038f9a5d4427c961a04b0bbe95641 Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Mon, 7 Nov 2022 23:28:06 +0900 Subject: [PATCH 3/7] fix parsing attributes for argument more --- res_syntax/src/res_core.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/res_syntax/src/res_core.ml b/res_syntax/src/res_core.ml index 0c34c9f053..82f710370f 100644 --- a/res_syntax/src/res_core.ml +++ b/res_syntax/src/res_core.ml @@ -1569,7 +1569,7 @@ and parseParameter p = Ast_helper.Pat.constraint_ ~attrs:(propLocAttr :: attrs) ~loc pat typ in - (attrs, Asttypes.Labelled lblName, pat) + ([], Asttypes.Labelled lblName, pat) | As -> Parser.next p; let pat = @@ -1585,7 +1585,8 @@ and parseParameter p = let loc = mkLoc startPos p.prevEndPos in ( [], Asttypes.Labelled lblName, - Ast_helper.Pat.var ~loc (Location.mkloc lblName loc) )) + Ast_helper.Pat.var ~attrs:(propLocAttr :: attrs) ~loc + (Location.mkloc lblName loc) )) | _ -> let pattern = parseConstrainedPattern p in let attrs = List.concat [pattern.ppat_attributes; attrs] in From 42468e0d12b1a2493c357ea9c2b6a0382b51381a Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Mon, 7 Nov 2022 23:29:21 +0900 Subject: [PATCH 4/7] update changelog of res_syntax --- res_syntax/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/res_syntax/CHANGELOG.md b/res_syntax/CHANGELOG.md index e12a288505..c679f8a541 100644 --- a/res_syntax/CHANGELOG.md +++ b/res_syntax/CHANGELOG.md @@ -13,6 +13,7 @@ #### :boom: Breaking Change - Emit an error when a `@string` or `@int` attribute is used in a V4 component https://github.com/rescript-lang/rescript-compiler/issues/5724 +- Parse the attributes of labelled argument to the pattern attributes of argument instead of function. #### :rocket: New Feature From 917e680383a307689ce2f4aabd23a87d2076115a Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Tue, 8 Nov 2022 01:59:13 +0900 Subject: [PATCH 5/7] add more tricky tests --- res_syntax/tests/printer/expr/asyncAwait.res | 4 ++++ res_syntax/tests/printer/expr/expected/asyncAwait.res.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/res_syntax/tests/printer/expr/asyncAwait.res b/res_syntax/tests/printer/expr/asyncAwait.res index a410e5ef5a..c80d6f1fb4 100644 --- a/res_syntax/tests/printer/expr/asyncAwait.res +++ b/res_syntax/tests/printer/expr/asyncAwait.res @@ -113,3 +113,7 @@ let b1 = await (3+4) let b2 = await (3**4) let b3 = await (foo->bar(~arg)) let b4 = await (foo.bar.baz) + +let c1 = @foo x => @bar y => x + y +let c2 = (. x) => y => x+y +let c3 = (. x) => @foo y => x+y \ No newline at end of file diff --git a/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt b/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt index 94c0d12054..4a51a963a1 100644 --- a/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt +++ b/res_syntax/tests/printer/expr/expected/asyncAwait.res.txt @@ -135,3 +135,7 @@ let b1 = await (3 + 4) let b2 = await (3 ** 4) let b3 = await foo->bar(~arg) let b4 = await foo.bar.baz + +let c1 = @foo x => @bar y => x + y +let c2 = (. x, y) => x + y +let c3 = (. x) => @foo y => x + y From 78206f49258d01630de81e370c1a4079ad2b2386 Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Tue, 8 Nov 2022 02:01:43 +0900 Subject: [PATCH 6/7] move the changelog to compiler --- CHANGELOG.md | 1 + res_syntax/CHANGELOG.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a89380d17..61ef2bd585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - `npm i -g rescript@9` - `rescript convert ` - Remove obsolete built-in project templates and the "rescript init" functionality. This will be replaced by the create-rescript-app project that is maintained separately. +- Parse the attributes of labelled argument to the pattern attributes of argument instead of function. #### :boom: Breaking Change diff --git a/res_syntax/CHANGELOG.md b/res_syntax/CHANGELOG.md index c679f8a541..e12a288505 100644 --- a/res_syntax/CHANGELOG.md +++ b/res_syntax/CHANGELOG.md @@ -13,7 +13,6 @@ #### :boom: Breaking Change - Emit an error when a `@string` or `@int` attribute is used in a V4 component https://github.com/rescript-lang/rescript-compiler/issues/5724 -- Parse the attributes of labelled argument to the pattern attributes of argument instead of function. #### :rocket: New Feature From 6251a14119b65ea66e4e50dfb411adb450015edd Mon Sep 17 00:00:00 2001 From: Woonki Moon Date: Tue, 8 Nov 2022 02:16:59 +0900 Subject: [PATCH 7/7] add comments --- res_syntax/src/res_parsetree_viewer.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res_syntax/src/res_parsetree_viewer.ml b/res_syntax/src/res_parsetree_viewer.ml index be0cd813e7..374ec7b2cd 100644 --- a/res_syntax/src/res_parsetree_viewer.ml +++ b/res_syntax/src/res_parsetree_viewer.ml @@ -157,6 +157,8 @@ let funExpr expr = } -> let parameter = Parameter {attrs = []; lbl; defaultExpr; pat = pattern} in collect attrsBefore (parameter :: acc) returnExpr + (* If a fun has an attribute, then it stops here and makes currying. + i.e attributes outside of (...), uncurried `(.)` and `async` make currying *) | {pexp_desc = Pexp_fun _} -> (attrsBefore, List.rev acc, expr) | expr -> (attrsBefore, List.rev acc, expr) in