diff --git a/CHANGELOG.md b/CHANGELOG.md index 064b2d34..5d7f0a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,7 @@ - Fix issue where the JSX key type is not an optional string https://github.com/rescript-lang/syntax/pull/693 - Fix issue where the JSX fragment without children build error https://github.com/rescript-lang/syntax/pull/704 - Fix issue where async as an id cannot be used with application and labelled arguments https://github.com/rescript-lang/syntax/issues/707 - +- Treat await as almost-unary operator weaker than pipe so `await foo->bar` means `await (foo->bar)` https://github.com/rescript-lang/syntax/pull/711 #### :eyeglasses: Spec Compliance diff --git a/src/res_core.ml b/src/res_core.ml index d535487d..e4048594 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -3134,7 +3134,8 @@ and parseAwaitExpression p = let awaitLoc = mkLoc p.Parser.startPos p.endPos in let awaitAttr = makeAwaitAttr awaitLoc in Parser.expect Await p; - let expr = parseUnaryExpr p in + let tokenPrec = Token.precedence MinusGreater in + let expr = parseBinaryExpr ~context:OrdinaryExpr p tokenPrec in { expr with pexp_attributes = awaitAttr :: expr.pexp_attributes; diff --git a/src/res_parens.ml b/src/res_parens.ml index c18b7565..355418b4 100644 --- a/src/res_parens.ml +++ b/src/res_parens.ml @@ -175,7 +175,11 @@ let flattenOperandRhs parentOperator rhs = | _ when ParsetreeViewer.isTernaryExpr rhs -> true | _ -> false -let lazyOrAssertOrAwaitExprRhs expr = +let binaryOperatorInsideAwaitNeedsParens operator = + ParsetreeViewer.operatorPrecedence operator + < ParsetreeViewer.operatorPrecedence "|." + +let lazyOrAssertOrAwaitExprRhs ?(inAwait = false) expr = let optBraces, _ = ParsetreeViewer.processBracesAttr expr in match optBraces with | Some ({Location.loc = bracesLoc}, _) -> Braced bracesLoc @@ -186,7 +190,14 @@ let lazyOrAssertOrAwaitExprRhs expr = | _ :: _ -> true | [] -> false -> Parenthesized - | expr when ParsetreeViewer.isBinaryExpression expr -> Parenthesized + | { + pexp_desc = + Pexp_apply ({pexp_desc = Pexp_ident {txt = Longident.Lident operator}}, _); + } + when ParsetreeViewer.isBinaryExpression expr -> + if inAwait && not (binaryOperatorInsideAwaitNeedsParens operator) then + Nothing + else Parenthesized | { pexp_desc = Pexp_constraint ({pexp_desc = Pexp_pack _}, {ptyp_desc = Ptyp_package _}); @@ -202,7 +213,9 @@ let lazyOrAssertOrAwaitExprRhs expr = | Pexp_try _ | Pexp_while _ | Pexp_for _ | Pexp_ifthenelse _ ); } -> Parenthesized - | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes -> + | _ + when (not inAwait) + && ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes -> Parenthesized | _ -> Nothing) diff --git a/src/res_parens.mli b/src/res_parens.mli index cedf98e1..6f705432 100644 --- a/src/res_parens.mli +++ b/src/res_parens.mli @@ -10,7 +10,8 @@ val subBinaryExprOperand : string -> string -> bool val rhsBinaryExprOperand : string -> Parsetree.expression -> bool val flattenOperandRhs : string -> Parsetree.expression -> bool -val lazyOrAssertOrAwaitExprRhs : Parsetree.expression -> kind +val binaryOperatorInsideAwaitNeedsParens : string -> bool +val lazyOrAssertOrAwaitExprRhs : ?inAwait:bool -> Parsetree.expression -> kind val fieldExpr : Parsetree.expression -> kind diff --git a/src/res_printer.ml b/src/res_printer.ml index 8179ca0f..9925568a 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -3330,13 +3330,13 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = if ParsetreeViewer.hasAwaitAttribute e.pexp_attributes then let rhs = match - Parens.lazyOrAssertOrAwaitExprRhs + Parens.lazyOrAssertOrAwaitExprRhs ~inAwait:true { e with pexp_attributes = List.filter (function - | {Location.txt = "res.await" | "ns.braces"}, _ -> false + | {Location.txt = "ns.braces"}, _ -> false | _ -> true) e.pexp_attributes; } @@ -3612,13 +3612,18 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl = in let doc = if isAwait then + let parens = + Res_parens.binaryOperatorInsideAwaitNeedsParens operator + in Doc.concat [ - Doc.text "await "; Doc.lparen; + Doc.text "await "; + (if parens then Doc.lparen else Doc.nil); leftPrinted; printBinaryOperator ~inlineRhs:false operator; rightPrinted; + (if parens then Doc.rparen else Doc.nil); Doc.rparen; ] else diff --git a/tests/parsing/grammar/expressions/async.res b/tests/parsing/grammar/expressions/async.res index 9f33ef2d..0a499bad 100644 --- a/tests/parsing/grammar/expressions/async.res +++ b/tests/parsing/grammar/expressions/async.res @@ -29,4 +29,9 @@ let async = { let f = isPositive ? (async (a, b) : int => a + b) : async (c, d) : int => c - d let foo = async(~a=34) -let bar = async(~a)=>a+1 \ No newline at end of file +let bar = async(~a)=>a+1 + +let ex1 = await 3 + await 4 +let ex2 = await 3 ** await 4 +let ex3 = await foo->bar(~arg) +let ex4 = await foo.bar.baz diff --git a/tests/parsing/grammar/expressions/expected/async.res.txt b/tests/parsing/grammar/expressions/expected/async.res.txt index eb4424d9..fe86bf4d 100644 --- a/tests/parsing/grammar/expressions/expected/async.res.txt +++ b/tests/parsing/grammar/expressions/expected/async.res.txt @@ -27,4 +27,8 @@ let f = else (((fun c -> fun d -> (c - d : int)))[@res.async ])) [@ns.ternary ]) let foo = async ~a:((34)[@ns.namedArgLoc ]) -let bar = ((fun ~a:((a)[@ns.namedArgLoc ]) -> a + 1)[@res.async ]) \ No newline at end of file +let bar = ((fun ~a:((a)[@ns.namedArgLoc ]) -> a + 1)[@res.async ]) +let ex1 = ((3)[@res.await ]) + ((4)[@res.await ]) +let ex2 = ((3)[@res.await ]) ** ((4)[@res.await ]) +let ex3 = ((foo |. (bar ~arg:((arg)[@ns.namedArgLoc ])))[@res.await ]) +let ex4 = (((foo.bar).baz)[@res.await ]) \ No newline at end of file diff --git a/tests/printer/expr/asyncAwait.res b/tests/printer/expr/asyncAwait.res index efabc856..a410e5ef 100644 --- a/tests/printer/expr/asyncAwait.res +++ b/tests/printer/expr/asyncAwait.res @@ -99,6 +99,17 @@ let f13 = @a @b (~x) => 3 let aw = (await (server->start))->foo let aw = (@foo (server->start))->foo +let aw = (await (3**4))->foo let foo = async(~a=34) -let bar = async(~a)=>a+1 \ No newline at end of file +let bar = async(~a)=>a+1 + +let a1 = await 3 + await 4 +let a2 = await 3 ** await 4 +let a3 = await foo->bar(~arg) +let a4 = await foo.bar.baz + +let b1 = await (3+4) +let b2 = await (3**4) +let b3 = await (foo->bar(~arg)) +let b4 = await (foo.bar.baz) diff --git a/tests/printer/expr/expected/asyncAwait.res.txt b/tests/printer/expr/expected/asyncAwait.res.txt index 1082a3d8..e9f2b82c 100644 --- a/tests/printer/expr/expected/asyncAwait.res.txt +++ b/tests/printer/expr/expected/asyncAwait.res.txt @@ -30,14 +30,14 @@ user.data = await fetch() {await weirdReactSuspenseApi} -let inBinaryExpression = (await x)->Js.Promise.resolve + 1 -let inBinaryExpression = (await x)->Js.Promise.resolve + (await y)->Js.Promise.resolve +let inBinaryExpression = await x->Js.Promise.resolve + 1 +let inBinaryExpression = await x->Js.Promise.resolve + await y->Js.Promise.resolve let withCallback = async (. ()) => { - async (. x) => await (x->Js.promise.resolve) + 1 + async (. x) => await x->Js.promise.resolve + 1 } -let () = (await fetch(url))->(await resolve) +let () = await (await fetch(url))->(await resolve) let _ = await (1 + 1) let _ = (await 1) + 1 @@ -119,8 +119,19 @@ let f11 = (. ~x) => (. ~y) => 3 let f12 = @a x => 3 let f13 = (@a @b ~x) => 3 -let aw = await (server->start)->foo +let aw = (await server->start)->foo let aw = @foo (server->start)->foo +let aw = (await (3 ** 4))->foo let foo = async(~a=34) let bar = async (~a) => a + 1 + +let a1 = (await 3) + (await 4) +let a2 = (await 3) ** (await 4) +let a3 = await foo->bar(~arg) +let a4 = await foo.bar.baz + +let b1 = await (3 + 4) +let b2 = await (3 ** 4) +let b3 = await foo->bar(~arg) +let b4 = await foo.bar.baz