diff --git a/packages/flow-parser/test/custom_ast_types.js b/packages/flow-parser/test/custom_ast_types.js index 9b0538b049d..df5b88a60dc 100644 --- a/packages/flow-parser/test/custom_ast_types.js +++ b/packages/flow-parser/test/custom_ast_types.js @@ -232,3 +232,13 @@ def('Import') def('CallExpression') .field('callee', or(def('Expression'), def('Import'))); + +def('OptionalMemberExpression') + .bases("MemberExpression") + .build("optional") + .field("optional", Boolean) + +def('OptionalCallExpression') + .bases("CallExpression") + .build("optional") + .field("optional", Boolean) diff --git a/packages/flow-parser/test/esprima_test_runner.js b/packages/flow-parser/test/esprima_test_runner.js index a034ecdd059..1a61409210c 100644 --- a/packages/flow-parser/test/esprima_test_runner.js +++ b/packages/flow-parser/test/esprima_test_runner.js @@ -260,13 +260,6 @@ function handleSpecialObjectCompare(esprima, flow, env) { delete param.typeAnnotation; delete param.optional; } - /* Esprima doesn't implement optional chaining, so ignore the `optional` - * fields on `Member` and `Call` Flow AST nodes. - */ - case 'MemberExpression': - case 'CallExpression': - delete flow.optional; - break; } switch (esprima.type) { diff --git a/src/common/reason.ml b/src/common/reason.ml index 353f9cd77dc..406ad56db04 100644 --- a/src/common/reason.ml +++ b/src/common/reason.ml @@ -783,7 +783,7 @@ end) * In the first example we need to wrap 1 + 2 to correctly print the property * access. However, we don't need to wrap o in o.p. In o[1 + 2] we don't need to * wrap 1 + 2 since it is already wrapped in a sense. *) -let rec code_desc_of_expression ~wrap (_, x) = +let rec code_desc_of_expression ~wrap (loc, x) = let do_wrap = if wrap then (fun s -> "(" ^ s ^ ")") else (fun s -> s) in Ast.Expression.(match x with | Array { Array.elements = []; _ } -> "[]" @@ -815,9 +815,9 @@ Ast.Expression.(match x with do_wrap (left ^ " " ^ operator ^ " " ^ right) | Binary { Binary.operator; left; right } -> do_wrap (code_desc_of_operation left (`Binary operator) right) -| Call { Call.callee; arguments = []; optional = _ } -> +| Call { Call.callee; arguments = [] } -> (code_desc_of_expression ~wrap:true callee) ^ "()" -| Call { Call.callee; arguments = _; optional = _ } -> +| Call { Call.callee; arguments = _ } -> (code_desc_of_expression ~wrap:true callee) ^ "(...)" | Class _ -> "class { ... }" | Conditional { Conditional.test; consequent; alternate } -> @@ -835,7 +835,7 @@ Ast.Expression.(match x with | Ast.Expression.Literal x -> code_desc_of_literal x | Logical { Logical.operator; left; right } -> do_wrap (code_desc_of_operation left (`Logical operator) right) -| Member { Member._object; property; computed = _; optional = _ } -> Member.( +| Member { Member._object; property; computed = _ } -> Member.( let o = code_desc_of_expression ~wrap:true _object in o ^ (match property with | PropertyIdentifier (_, x) -> "." ^ x @@ -848,6 +848,16 @@ Ast.Expression.(match x with | New { New.callee; arguments = _ } -> "new " ^ (code_desc_of_expression ~wrap:true callee) ^ "(...)" | Object _ -> "{...}" +| OptionalCall { OptionalCall.call; optional } -> + let call_desc = code_desc_of_expression ~wrap:false (loc, Call call) in + if optional then "?." ^ call_desc else call_desc +| OptionalMember { OptionalMember.member; optional } -> + let member_desc = code_desc_of_expression ~wrap:false (loc, Member member) in + if optional + then Member.(match member.property with + | PropertyExpression _ -> "?." ^ member_desc + | PropertyIdentifier _ | PropertyPrivateName _ -> "?" ^ member_desc + ) else member_desc | Sequence { Sequence.expressions } -> code_desc_of_expression ~wrap (List.hd (List.rev expressions)) | Super -> "super" diff --git a/src/parser/ast.ml b/src/parser/ast.ml index 13911a2673f..8b2abbfe63b 100644 --- a/src/parser/ast.ml +++ b/src/parser/ast.ml @@ -723,6 +723,11 @@ and Expression : sig type 'M t = { callee: 'M Expression.t; arguments: 'M expression_or_spread list; + } + end + module OptionalCall : sig + type 'M t = { + call: 'M Call.t; optional: bool; } end @@ -735,6 +740,11 @@ and Expression : sig _object: 'M Expression.t; property: 'M property; computed: bool; + } + end + module OptionalMember : sig + type 'M t = { + member: 'M Member.t; optional: bool; } end @@ -799,6 +809,8 @@ and Expression : sig | MetaProperty of 'M MetaProperty.t | New of 'M New.t | Object of 'M Object.t + | OptionalCall of 'M OptionalCall.t + | OptionalMember of 'M OptionalMember.t | Sequence of 'M Sequence.t | Super | TaggedTemplate of 'M TaggedTemplate.t diff --git a/src/parser/estree_translator.ml b/src/parser/estree_translator.ml index b2e6a2b499a..dac07015a99 100644 --- a/src/parser/estree_translator.ml +++ b/src/parser/estree_translator.ml @@ -470,25 +470,19 @@ end with type t = Impl.t) = struct "arguments", array_of_list expression_or_spread _new.arguments; ] ) - | loc, Call call -> Call.( - node "CallExpression" loc [ - "callee", expression call.callee; - "arguments", array_of_list expression_or_spread call.arguments; - "optional", bool call.optional; - ] + | loc, Call call -> + node "CallExpression" loc (call_node_properties call) + | loc, OptionalCall opt_call -> OptionalCall.( + node "OptionalCallExpression" loc (call_node_properties opt_call.call @ [ + "optional", bool opt_call.optional; + ]) ) - | loc, Member member -> Member.( - let property = match member.property with - | PropertyIdentifier id -> identifier id - | PropertyPrivateName name -> private_name name - | PropertyExpression expr -> expression expr - in - node "MemberExpression" loc [ - "object", expression member._object; - "property", property; - "computed", bool member.computed; - "optional", bool member.optional; - ] + | loc, Member member -> + node "MemberExpression" loc (member_node_properties member) + | loc, OptionalMember opt_member -> OptionalMember.( + node "OptionalMemberExpression" loc (member_node_properties opt_member.member @ [ + "optional", bool opt_member.optional; + ]) ) | loc, Yield yield -> Yield.( node "YieldExpression" loc [ @@ -1447,4 +1441,22 @@ end with type t = Impl.t) = struct in node _type loc value ) + + and call_node_properties call = Expression.Call.([ + "callee", expression call.callee; + "arguments", array_of_list expression_or_spread call.arguments; + ]) + + and member_node_properties member = Expression.Member.( + let property = match member.property with + | PropertyIdentifier id -> identifier id + | PropertyPrivateName name -> private_name name + | PropertyExpression expr -> expression expr + in + [ + "object", expression member._object; + "property", property; + "computed", bool member.computed; + ] + ) end diff --git a/src/parser/expression_parser.ml b/src/parser/expression_parser.ml index 8f3b6fe7b8b..5ebb5f913ed 100644 --- a/src/parser/expression_parser.ml +++ b/src/parser/expression_parser.ml @@ -60,6 +60,8 @@ module Expression | _, Literal _ | _, Logical _ | _, New _ + | _, OptionalCall _ + | _, OptionalMember _ | _, Sequence _ | _, Super | _, TaggedTemplate _ @@ -212,6 +214,8 @@ module Expression | _, Logical _ | _, New _ | _, Object _ + | _, OptionalCall _ + | _, OptionalMember _ | _, Sequence _ | _, Super | _, TaggedTemplate _ @@ -532,12 +536,19 @@ module Expression | T_LPAREN when not (no_call env) -> let args_loc, arguments = arguments env in let loc = Loc.btwn start_loc args_loc in - call_cover ~allow_optional_chain ~in_optional_chain env start_loc - (Cover_expr (loc, Expression.(Call { Call. - callee = as_expression env left; - arguments; + let call = { Expression.Call. + callee = as_expression env left; + arguments; + } in + let call = if in_optional_chain + then Expression.(OptionalCall { OptionalCall. + call; optional; - }))) + }) + else Expression.Call call + in + call_cover ~allow_optional_chain ~in_optional_chain env start_loc + (Cover_expr (loc, call)) | _ -> left and call ?(allow_optional_chain=true) env start_loc left = @@ -625,13 +636,20 @@ module Expression let last_loc = Peek.loc env in Expect.token env T_RBRACKET; let loc = Loc.btwn start_loc last_loc in - call_cover ~allow_optional_chain ~in_optional_chain env start_loc - (Cover_expr (loc, Expression.(Member { Member. - _object = as_expression env left; - property = Member.PropertyExpression expr; - computed = true; + let member = Expression.Member.({ + _object = as_expression env left; + property = PropertyExpression expr; + computed = true; + }) in + let member = if in_optional_chain + then Expression.(OptionalMember { OptionalMember. + member; optional; - }))) + }) + else Expression.Member member + in + call_cover ~allow_optional_chain ~in_optional_chain env start_loc + (Cover_expr (loc, member)) in let static ?(allow_optional_chain=true) ?(in_optional_chain=false) ?(optional=false) env start_loc left = @@ -646,13 +664,20 @@ module Expression | Cover_expr (_, Ast.Expression.Super) when is_private -> error_at env (loc, Error.SuperPrivate) | _ -> () end; - call_cover ~allow_optional_chain ~in_optional_chain env start_loc - (Cover_expr (loc, Expression.(Member { Member. - _object = as_expression env left; - property; - computed = false; + let member = Expression.Member.({ + _object = as_expression env left; + property; + computed = false; + }) in + let member = if in_optional_chain + then Expression.(OptionalMember { OptionalMember. + member; optional; - }))) + }) + else Expression.Member member + in + call_cover ~allow_optional_chain ~in_optional_chain env start_loc + (Cover_expr (loc, member)) in fun ?(allow_optional_chain=true) ?(in_optional_chain=false) env start_loc left -> let options = parse_options env in diff --git a/src/parser/parse_error.ml b/src/parser/parse_error.ml index 0e575ff9bca..fb1169413cc 100644 --- a/src/parser/parse_error.ml +++ b/src/parser/parse_error.ml @@ -279,6 +279,6 @@ module PP = use the optional chaining operator (`?.`). Optional chaining is an active early-stage \ feature proposal which may change and is not enabled by default. To enable support in \ the parser, use the `esproposal_optional_chaining` option." - | OptionalChainNew -> "`new` may not be combined with an optional chain." + | OptionalChainNew -> "An optional chain may not be used in a `new` expression." | OptionalChainTemplate -> "Template literals may not be used in an optional chain." end diff --git a/src/parser/test/flow/optional_chaining/class-constructor-call.js b/src/parser/test/flow/optional_chaining/class-constructor-call.js index 7282eedc213..39c849c304b 100644 --- a/src/parser/test/flow/optional_chaining/class-constructor-call.js +++ b/src/parser/test/flow/optional_chaining/class-constructor-call.js @@ -9,3 +9,5 @@ new B?.C?.(a, b) new B?.C new B?.C() + +new C?.b.d() diff --git a/src/parser/test/flow/optional_chaining/class-constructor-call.tree.json b/src/parser/test/flow/optional_chaining/class-constructor-call.tree.json index fcd223e2451..dde2907f03a 100644 --- a/src/parser/test/flow/optional_chaining/class-constructor-call.tree.json +++ b/src/parser/test/flow/optional_chaining/class-constructor-call.tree.json @@ -2,40 +2,44 @@ "errors":[ { "loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":3,"column":5},"end":{"line":3,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":5,"column":5},"end":{"line":5,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":5,"column":8},"end":{"line":5,"column":10}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":7,"column":5},"end":{"line":7,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":7,"column":8},"end":{"line":7,"column":10}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":9,"column":5},"end":{"line":9,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." }, { "loc":{"source":null,"start":{"line":11,"column":5},"end":{"line":11,"column":7}}, - "message":"`new` may not be combined with an optional chain." + "message":"An optional chain may not be used in a `new` expression." + }, + { + "loc":{"source":null,"start":{"line":13,"column":5},"end":{"line":13,"column":7}}, + "message":"An optional chain may not be used in a `new` expression." } ], "type":"Program", - "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":11,"column":10}}, - "range":[0,78], + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":13,"column":12}}, + "range":[0,92], "body":[ { "type":"ExpressionStatement", @@ -103,7 +107,7 @@ "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":12}}, "range":[26,38], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":5,"column":4},"end":{"line":5,"column":8}}, "range":[30,34], "object":{ @@ -138,7 +142,7 @@ "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":16}}, "range":[40,56], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":7,"column":4},"end":{"line":7,"column":8}}, "range":[44,48], "object":{ @@ -190,7 +194,7 @@ "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":8}}, "range":[58,66], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":9,"column":4},"end":{"line":9,"column":8}}, "range":[62,66], "object":{ @@ -225,7 +229,7 @@ "loc":{"source":null,"start":{"line":11,"column":0},"end":{"line":11,"column":10}}, "range":[68,78], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":11,"column":4},"end":{"line":11,"column":8}}, "range":[72,76], "object":{ @@ -250,6 +254,56 @@ "arguments":[] }, "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":13,"column":0},"end":{"line":13,"column":12}}, + "range":[80,92], + "expression":{ + "type":"NewExpression", + "loc":{"source":null,"start":{"line":13,"column":0},"end":{"line":13,"column":12}}, + "range":[80,92], + "callee":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":13,"column":4},"end":{"line":13,"column":10}}, + "range":[84,90], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":13,"column":4},"end":{"line":13,"column":8}}, + "range":[84,88], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":13,"column":4},"end":{"line":13,"column":5}}, + "range":[84,85], + "name":"C", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":13,"column":7},"end":{"line":13,"column":8}}, + "range":[87,88], + "name":"b", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":13,"column":9},"end":{"line":13,"column":10}}, + "range":[89,90], + "name":"d", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":false + }, + "arguments":[] + }, + "directive":null } ], "comments":[] diff --git a/src/parser/test/flow/optional_chaining/function-call.js b/src/parser/test/flow/optional_chaining/function-call.js index ccf7cad6ee4..a8e5c89d1c3 100644 --- a/src/parser/test/flow/optional_chaining/function-call.js +++ b/src/parser/test/flow/optional_chaining/function-call.js @@ -5,3 +5,5 @@ func?.(a, b) a?.func?.() a?.func?.(a, b) + +a.func?.() diff --git a/src/parser/test/flow/optional_chaining/function-call.tree.json b/src/parser/test/flow/optional_chaining/function-call.tree.json index 7a53899eaea..b9b6e7b3b95 100644 --- a/src/parser/test/flow/optional_chaining/function-call.tree.json +++ b/src/parser/test/flow/optional_chaining/function-call.tree.json @@ -1,7 +1,7 @@ { "type":"Program", - "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":7,"column":15}}, - "range":[0,52], + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":9,"column":10}}, + "range":[0,64], "body":[ { "type":"ExpressionStatement", @@ -19,8 +19,7 @@ "typeAnnotation":null, "optional":false }, - "arguments":[], - "optional":true + "arguments":[] }, "directive":null }, @@ -57,8 +56,7 @@ "typeAnnotation":null, "optional":false } - ], - "optional":true + ] }, "directive":null }, @@ -67,11 +65,11 @@ "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":11}}, "range":[24,35], "expression":{ - "type":"CallExpression", + "type":"OptionalCallExpression", "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":11}}, "range":[24,35], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":7}}, "range":[24,31], "object":{ @@ -103,11 +101,11 @@ "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":15}}, "range":[37,52], "expression":{ - "type":"CallExpression", + "type":"OptionalCallExpression", "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":15}}, "range":[37,52], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":7}}, "range":[37,44], "object":{ @@ -150,6 +148,40 @@ "optional":true }, "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":10}}, + "range":[54,64], + "expression":{ + "type":"CallExpression", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":10}}, + "range":[54,64], + "callee":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":6}}, + "range":[54,60], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":1}}, + "range":[54,55], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":9,"column":2},"end":{"line":9,"column":6}}, + "range":[56,60], + "name":"func", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "arguments":[] + }, + "directive":null } ], "comments":[] diff --git a/src/parser/test/flow/optional_chaining/member-access-bracket.js b/src/parser/test/flow/optional_chaining/member-access-bracket.js index ee45bfdb9e9..3c4500913f8 100644 --- a/src/parser/test/flow/optional_chaining/member-access-bracket.js +++ b/src/parser/test/flow/optional_chaining/member-access-bracket.js @@ -5,3 +5,7 @@ obj?.[expr]?.[other] obj?.[true] obj?.[true]?.[true] + +obj.a?.[expr] + +obj.a?.[true] diff --git a/src/parser/test/flow/optional_chaining/member-access-bracket.tree.json b/src/parser/test/flow/optional_chaining/member-access-bracket.tree.json index 59f1e270309..1af61d45e12 100644 --- a/src/parser/test/flow/optional_chaining/member-access-bracket.tree.json +++ b/src/parser/test/flow/optional_chaining/member-access-bracket.tree.json @@ -1,14 +1,14 @@ { "type":"Program", - "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":7,"column":19}}, - "range":[0,67], + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":11,"column":13}}, + "range":[0,97], "body":[ { "type":"ExpressionStatement", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":11}}, "range":[0,11], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":11}}, "range":[0,11], "object":{ @@ -37,11 +37,11 @@ "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":20}}, "range":[13,33], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":20}}, "range":[13,33], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":11}}, "range":[13,24], "object":{ @@ -81,7 +81,7 @@ "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":11}}, "range":[35,46], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":11}}, "range":[35,46], "object":{ @@ -109,11 +109,11 @@ "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":19}}, "range":[48,67], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":19}}, "range":[48,67], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":11}}, "range":[48,59], "object":{ @@ -145,6 +145,91 @@ "optional":true }, "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":13}}, + "range":[69,82], + "expression":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":13}}, + "range":[69,82], + "object":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":5}}, + "range":[69,74], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":9,"column":0},"end":{"line":9,"column":3}}, + "range":[69,72], + "name":"obj", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":9,"column":4},"end":{"line":9,"column":5}}, + "range":[73,74], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":9,"column":8},"end":{"line":9,"column":12}}, + "range":[77,81], + "name":"expr", + "typeAnnotation":null, + "optional":false + }, + "computed":true, + "optional":true + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":11,"column":0},"end":{"line":11,"column":13}}, + "range":[84,97], + "expression":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":11,"column":0},"end":{"line":11,"column":13}}, + "range":[84,97], + "object":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":11,"column":0},"end":{"line":11,"column":5}}, + "range":[84,89], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":11,"column":0},"end":{"line":11,"column":3}}, + "range":[84,87], + "name":"obj", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":11,"column":4},"end":{"line":11,"column":5}}, + "range":[88,89], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "property":{ + "type":"Literal", + "loc":{"source":null,"start":{"line":11,"column":8},"end":{"line":11,"column":12}}, + "range":[92,96], + "value":true, + "raw":"true" + }, + "computed":true, + "optional":true + }, + "directive":null } ], "comments":[] diff --git a/src/parser/test/flow/optional_chaining/member-access.js b/src/parser/test/flow/optional_chaining/member-access.js index 8e44f574153..d4e290a8990 100644 --- a/src/parser/test/flow/optional_chaining/member-access.js +++ b/src/parser/test/flow/optional_chaining/member-access.js @@ -1,3 +1,5 @@ foo?.bar foo?.bar?.baz + +foo.bar?.baz diff --git a/src/parser/test/flow/optional_chaining/member-access.tree.json b/src/parser/test/flow/optional_chaining/member-access.tree.json index ab8fd0dc893..f560f7d448e 100644 --- a/src/parser/test/flow/optional_chaining/member-access.tree.json +++ b/src/parser/test/flow/optional_chaining/member-access.tree.json @@ -1,14 +1,14 @@ { "type":"Program", - "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":3,"column":13}}, - "range":[0,23], + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":5,"column":12}}, + "range":[0,37], "body":[ { "type":"ExpressionStatement", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, "range":[0,8], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, "range":[0,8], "object":{ @@ -37,11 +37,11 @@ "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":13}}, "range":[10,23], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":13}}, "range":[10,23], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":8}}, "range":[10,18], "object":{ @@ -75,6 +75,49 @@ "optional":true }, "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":12}}, + "range":[25,37], + "expression":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":12}}, + "range":[25,37], + "object":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":7}}, + "range":[25,32], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":3}}, + "range":[25,28], + "name":"foo", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":4},"end":{"line":5,"column":7}}, + "range":[29,32], + "name":"bar", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":9},"end":{"line":5,"column":12}}, + "range":[34,37], + "name":"baz", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "directive":null } ], "comments":[] diff --git a/src/parser/test/flow/optional_chaining/missing-plugin.tree.json b/src/parser/test/flow/optional_chaining/missing-plugin.tree.json index c223ca64814..20896792647 100644 --- a/src/parser/test/flow/optional_chaining/missing-plugin.tree.json +++ b/src/parser/test/flow/optional_chaining/missing-plugin.tree.json @@ -14,7 +14,7 @@ "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":4}}, "range":[0,4], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":4}}, "range":[0,4], "object":{ diff --git a/src/parser/test/flow/optional_chaining/optional-chain-expression.js b/src/parser/test/flow/optional_chaining/optional-chain-expression.js new file mode 100644 index 00000000000..892df0d0cd0 --- /dev/null +++ b/src/parser/test/flow/optional_chaining/optional-chain-expression.js @@ -0,0 +1 @@ +a.b?.c() diff --git a/src/parser/test/flow/optional_chaining/optional-chain-expression.options.json b/src/parser/test/flow/optional_chaining/optional-chain-expression.options.json new file mode 100644 index 00000000000..6b037551fc5 --- /dev/null +++ b/src/parser/test/flow/optional_chaining/optional-chain-expression.options.json @@ -0,0 +1,3 @@ +{ + "esproposal_optional_chaining": true +} diff --git a/src/parser/test/flow/optional_chaining/optional-chain-expression.tree.json b/src/parser/test/flow/optional_chaining/optional-chain-expression.tree.json new file mode 100644 index 00000000000..d472bdae76f --- /dev/null +++ b/src/parser/test/flow/optional_chaining/optional-chain-expression.tree.json @@ -0,0 +1,58 @@ +{ + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, + "range":[0,8], + "body":[ + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, + "range":[0,8], + "expression":{ + "type":"OptionalCallExpression", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, + "range":[0,8], + "callee":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":6}}, + "range":[0,6], + "object":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":3}}, + "range":[0,3], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":1}}, + "range":[0,1], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":2},"end":{"line":1,"column":3}}, + "range":[2,3], + "name":"b", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":6}}, + "range":[5,6], + "name":"c", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "arguments":[], + "optional":false + }, + "directive":null + } + ], + "comments":[] +} diff --git a/src/parser/test/flow/optional_chaining/parenthesized-chain.js b/src/parser/test/flow/optional_chaining/parenthesized-chain.js new file mode 100644 index 00000000000..8e31fc28e60 --- /dev/null +++ b/src/parser/test/flow/optional_chaining/parenthesized-chain.js @@ -0,0 +1,5 @@ +(a?.b).c; + +(a?.b).c(); + +(a?.b)?.c.d?.e; diff --git a/src/parser/test/flow/optional_chaining/parenthesized-chain.options.json b/src/parser/test/flow/optional_chaining/parenthesized-chain.options.json new file mode 100644 index 00000000000..6b037551fc5 --- /dev/null +++ b/src/parser/test/flow/optional_chaining/parenthesized-chain.options.json @@ -0,0 +1,3 @@ +{ + "esproposal_optional_chaining": true +} diff --git a/src/parser/test/flow/optional_chaining/parenthesized-chain.tree.json b/src/parser/test/flow/optional_chaining/parenthesized-chain.tree.json new file mode 100644 index 00000000000..33f84603bca --- /dev/null +++ b/src/parser/test/flow/optional_chaining/parenthesized-chain.tree.json @@ -0,0 +1,174 @@ +{ + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":5,"column":15}}, + "range":[0,39], + "body":[ + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "range":[0,9], + "expression":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, + "range":[0,8], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":1,"column":1},"end":{"line":1,"column":5}}, + "range":[1,5], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":1},"end":{"line":1,"column":2}}, + "range":[1,2], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":4},"end":{"line":1,"column":5}}, + "range":[4,5], + "name":"b", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":7},"end":{"line":1,"column":8}}, + "range":[7,8], + "name":"c", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":11}}, + "range":[11,22], + "expression":{ + "type":"CallExpression", + "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":10}}, + "range":[11,21], + "callee":{ + "type":"MemberExpression", + "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":8}}, + "range":[11,19], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":3,"column":1},"end":{"line":3,"column":5}}, + "range":[12,16], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":3,"column":1},"end":{"line":3,"column":2}}, + "range":[12,13], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":3,"column":4},"end":{"line":3,"column":5}}, + "range":[15,16], + "name":"b", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":3,"column":7},"end":{"line":3,"column":8}}, + "range":[18,19], + "name":"c", + "typeAnnotation":null, + "optional":false + }, + "computed":false + }, + "arguments":[] + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":15}}, + "range":[24,39], + "expression":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":14}}, + "range":[24,38], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":11}}, + "range":[24,35], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":9}}, + "range":[24,33], + "object":{ + "type":"OptionalMemberExpression", + "loc":{"source":null,"start":{"line":5,"column":1},"end":{"line":5,"column":5}}, + "range":[25,29], + "object":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":1},"end":{"line":5,"column":2}}, + "range":[25,26], + "name":"a", + "typeAnnotation":null, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":4},"end":{"line":5,"column":5}}, + "range":[28,29], + "name":"b", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":8},"end":{"line":5,"column":9}}, + "range":[32,33], + "name":"c", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":10},"end":{"line":5,"column":11}}, + "range":[34,35], + "name":"d", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":false + }, + "property":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":13},"end":{"line":5,"column":14}}, + "range":[37,38], + "name":"e", + "typeAnnotation":null, + "optional":false + }, + "computed":false, + "optional":true + }, + "directive":null + } + ], + "comments":[] +} diff --git a/src/parser/test/flow/optional_chaining/separated-chaining.tree.json b/src/parser/test/flow/optional_chaining/separated-chaining.tree.json index b1c70415b44..46c0f2a6c99 100644 --- a/src/parser/test/flow/optional_chaining/separated-chaining.tree.json +++ b/src/parser/test/flow/optional_chaining/separated-chaining.tree.json @@ -8,23 +8,23 @@ "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, "range":[0,13], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, "range":[0,13], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":10}}, "range":[0,10], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":8}}, "range":[0,8], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":6}}, "range":[0,6], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":4}}, "range":[0,4], "object":{ @@ -97,15 +97,15 @@ "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":12}}, "range":[15,27], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":12}}, "range":[15,27], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":10}}, "range":[15,25], "object":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":8}}, "range":[15,23], "object":{ @@ -132,8 +132,7 @@ "typeAnnotation":null, "optional":false }, - "computed":false, - "optional":false + "computed":false }, "property":{ "type":"Identifier", @@ -143,8 +142,7 @@ "typeAnnotation":null, "optional":false }, - "computed":false, - "optional":false + "computed":false }, "property":{ "type":"Identifier", diff --git a/src/parser/test/flow/optional_chaining/template-literals.tree.json b/src/parser/test/flow/optional_chaining/template-literals.tree.json index 8879a1eaa05..2eac5ca8d9b 100644 --- a/src/parser/test/flow/optional_chaining/template-literals.tree.json +++ b/src/parser/test/flow/optional_chaining/template-literals.tree.json @@ -61,7 +61,7 @@ "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":9}}, "range":[10,19], "tag":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":4}}, "range":[10,14], "object":{ @@ -106,11 +106,11 @@ "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":12}}, "range":[21,33], "expression":{ - "type":"CallExpression", + "type":"OptionalCallExpression", "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":12}}, "range":[21,33], "callee":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":4}}, "range":[21,25], "object":{ @@ -171,7 +171,7 @@ "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":11}}, "range":[35,46], "expression":{ - "type":"MemberExpression", + "type":"OptionalMemberExpression", "loc":{"source":null,"start":{"line":7,"column":0},"end":{"line":7,"column":11}}, "range":[35,46], "object":{ diff --git a/src/parser/test/flow/private_class_properties/member.tree.json b/src/parser/test/flow/private_class_properties/member.tree.json index 93b9b7ebf4a..a05f3450c5b 100644 --- a/src/parser/test/flow/private_class_properties/member.tree.json +++ b/src/parser/test/flow/private_class_properties/member.tree.json @@ -133,8 +133,7 @@ "optional":false } }, - "computed":false, - "optional":false + "computed":false }, "directive":null }, @@ -163,8 +162,7 @@ "typeAnnotation":null, "optional":false }, - "computed":false, - "optional":false + "computed":false }, "property":{ "type":"PrivateName", @@ -179,8 +177,7 @@ "optional":false } }, - "computed":false, - "optional":false + "computed":false }, "directive":null } diff --git a/src/parser_utils/ast_builder.ml b/src/parser_utils/ast_builder.ml index 1ca8c53e75b..fe339a9c125 100644 --- a/src/parser_utils/ast_builder.ml +++ b/src/parser_utils/ast_builder.ml @@ -189,8 +189,16 @@ module Expressions = struct let identifier name = Loc.none, Identifier (Loc.none, name) - let call ?(optional=false) ?(args=[]) callee = - Loc.none, Call { Call.callee; arguments = args; optional } + let call_node ?(args=[]) callee = { Call.callee; arguments = args } + + let call ?(args=[]) callee = + Loc.none, Call (call_node ~args callee) + + let optional_call ~optional ?(args=[]) callee = + Loc.none, OptionalCall { OptionalCall. + call = call_node ~args callee; + optional; + } let function_ ?(generator=false) ?(params=[]) ?body () = let fn = Functions.make ~generator ~params ~id:None ?body ~expression:true () in @@ -255,34 +263,37 @@ module Expressions = struct Loc.none, Object { Object.properties } (* _object.property *) - let member ?(optional=false) ~property _object = + let member ~property _object = { Member. _object; property = Member.PropertyIdentifier (Loc.none, property); computed = false; - optional; } (* _object[property] *) - let member_computed ?(optional=false) ~property _object = + let member_computed ~property _object = { Member. _object; property = Member.PropertyIdentifier (Loc.none, property); computed = true; - optional; } - let member_computed_expr ?(optional=false) ~property _object = + let member_computed_expr ~property _object = { Member. _object; property = Member.PropertyExpression property; computed = true; - optional; } let member_expression expr = Loc.none, Ast.Expression.Member expr + let optional_member_expression ~optional expr = + Loc.none, OptionalMember { OptionalMember. + member = expr; + optional; + } + let new_ ?(args=[]) callee = Loc.none, New { New.callee; arguments = args } diff --git a/src/parser_utils/file_exports_resolver.ml b/src/parser_utils/file_exports_resolver.ml index a482b1e4773..0e34bdc6a5d 100644 --- a/src/parser_utils/file_exports_resolver.ml +++ b/src/parser_utils/file_exports_resolver.ml @@ -161,7 +161,6 @@ class exports_resolver ~ast = object(this) _object = module_loc, Identifier (_, "exports"); property; computed = _; - optional = _; })) (* module.exports.foo = ... *) | Assignment.Assign, (_, Ast.Pattern.Expression (_, Member { Member. @@ -171,7 +170,6 @@ class exports_resolver ~ast = object(this) }; property; computed = _; - optional = _; })) (* We only care about global scope *) when not (Scope_api.is_local_use scope_info module_loc) -> diff --git a/src/parser_utils/file_sig.ml b/src/parser_utils/file_sig.ml index ab1918ac5da..9dbb078cf06 100644 --- a/src/parser_utils/file_sig.ml +++ b/src/parser_utils/file_sig.ml @@ -363,7 +363,7 @@ class requires_calculator ~ast = object(this) method! member (expr: Loc.t Ast.Expression.Member.t) = let open Ast.Expression in let open Ast.Expression.Member in - let { _object; property; computed = _; optional = _ } = expr in + let { _object; property; computed = _ } = expr in (* Strip the loc to simplify the patterns *) let _, _object = _object in (* This gets called when patterns like `module.id` appear on the LHS of an @@ -393,7 +393,7 @@ class requires_calculator ~ast = object(this) method! call call_loc (expr: Loc.t Ast.Expression.Call.t) = let open Ast.Expression in - let { Call.callee; arguments; optional = _ } = expr in + let { Call.callee; arguments } = expr in this#handle_call call_loc callee arguments None; super#call call_loc expr @@ -691,7 +691,7 @@ class requires_calculator ~ast = object(this) | _ -> None end in begin match right with - | call_loc, Call { Call.callee; arguments; optional = _ } -> + | call_loc, Call { Call.callee; arguments } -> this#handle_call call_loc callee arguments bindings | _ -> () end diff --git a/src/parser_utils/flow_ast_mapper.ml b/src/parser_utils/flow_ast_mapper.ml index 7dbc5ef2889..30829bff25f 100644 --- a/src/parser_utils/flow_ast_mapper.ml +++ b/src/parser_utils/flow_ast_mapper.ml @@ -171,6 +171,8 @@ class mapper = object(this) | loc, MetaProperty x -> id this#meta_property x expr (fun x -> loc, MetaProperty x) | loc, New x -> id this#new_ x expr (fun x -> loc, New x) | loc, Object x -> id this#object_ x expr (fun x -> loc, Object x) + | loc, OptionalCall x -> id (this#optional_call loc) x expr (fun x -> loc, OptionalCall x) + | loc, OptionalMember x -> id this#optional_member x expr (fun x -> loc, OptionalMember x) | loc, Sequence x -> id this#sequence x expr (fun x -> loc, Sequence x) | loc, TaggedTemplate x -> id this#tagged_template x expr (fun x -> loc, TaggedTemplate x) | loc, TemplateLiteral x -> id this#template_literal x expr (fun x -> loc, TemplateLiteral x) @@ -219,11 +221,18 @@ class mapper = object(this) method call _loc (expr: Loc.t Ast.Expression.Call.t) = let open Ast.Expression.Call in - let { callee; arguments; optional } = expr in + let { callee; arguments } = expr in let callee' = this#expression callee in let arguments' = ListUtils.ident_map this#expression_or_spread arguments in if callee == callee' && arguments == arguments' then expr - else { callee = callee'; arguments = arguments'; optional } + else { callee = callee'; arguments = arguments' } + + method optional_call loc (expr: Loc.t Ast.Expression.OptionalCall.t) = + let open Ast.Expression.OptionalCall in + let { call; optional = _ } = expr in + let call' = this#call loc call in + if call == call' then expr + else { expr with call = call' } method catch_clause (clause: Loc.t Ast.Statement.Try.CatchClause.t') = let open Ast.Statement.Try.CatchClause in @@ -878,11 +887,18 @@ class mapper = object(this) method member (expr: Loc.t Ast.Expression.Member.t) = let open Ast.Expression.Member in - let { _object; property; computed = _; optional } = expr in + let { _object; property; computed = _ } = expr in let _object' = this#expression _object in let property' = this#member_property property in if _object == _object' && property == property' then expr - else { expr with _object = _object'; property = property'; optional } + else { expr with _object = _object'; property = property' } + + method optional_member (expr: Loc.t Ast.Expression.OptionalMember.t) = + let open Ast.Expression.OptionalMember in + let { member; optional = _ } = expr in + let member' = this#member member in + if member == member' then expr + else { expr with member = member' } method member_property (expr: Loc.t Ast.Expression.Member.property) = let open Ast.Expression.Member in diff --git a/src/parser_utils/output/__tests__/js_layout_generator_test.ml b/src/parser_utils/output/__tests__/js_layout_generator_test.ml index 786e1be8246..062bab85c98 100644 --- a/src/parser_utils/output/__tests__/js_layout_generator_test.ml +++ b/src/parser_utils/output/__tests__/js_layout_generator_test.ml @@ -417,7 +417,6 @@ let tests = "js_layout_generator" >::: [ _object = x; property = Ast.Expression.Member.PropertyIdentifier (Loc.none, "y"); computed = false; - optional = false; }) in assert_expression ~ctxt "new x.y()" member; diff --git a/src/parser_utils/output/js_layout_generator.ml b/src/parser_utils/output/js_layout_generator.ml index b93f0dcae37..dd089e527a5 100644 --- a/src/parser_utils/output/js_layout_generator.ml +++ b/src/parser_utils/output/js_layout_generator.ml @@ -92,9 +92,11 @@ let precedence_of_expression expr = (* Expressions involving operators *) | (_, E.Member _) + | (_, E.OptionalMember _) | (_, E.MetaProperty _) | (_, E.New _) -> 19 | (_, E.Call _) + | (_, E.OptionalCall _) | (_, E.TaggedTemplate _) | (_, E.Import _) -> 18 | (_, E.Update { E.Update.prefix = false; _ }) -> 17 @@ -711,35 +713,8 @@ and expression ?(ctxt=normal_context) ((loc, expr): Loc.t Ast.Expression.t) = expression_with_parens ~precedence ~ctxt right end; ] - | E.Call { E.Call.callee; arguments; optional } -> - let lparen = if optional then ".?(" else "(" in - begin match callee, arguments with - (* __d hack, force parens around factory function. - More details at: https://fburl.com/b1wv51vj - TODO: This is FB only, find generic way to add logic *) - | (_, E.Identifier (_, "__d")), [a; b; c; d] -> - fuse [ - Atom "__d"; - list - ~wrap:(Atom lparen, Atom ")") - ~sep:(Atom ",") - [ - expression_or_spread a; - expression_or_spread b; - wrap_in_parens (expression_or_spread c); - expression_or_spread d; - ] - ] - (* Standard call expression printing *) - | _ -> - fuse [ - expression_with_parens ~precedence ~ctxt callee; - list - ~wrap:(Atom lparen, Atom ")") - ~sep:(Atom ",") - (List.map expression_or_spread arguments) - ] - end + | E.Call c -> call ~precedence ~ctxt c + | E.OptionalCall { E.OptionalCall.call = c; optional } -> call ~optional ~precedence ~ctxt c | E.Conditional { E.Conditional.test; consequent; alternate } -> let test_layout = (* conditionals are right-associative *) @@ -776,29 +751,9 @@ and expression ?(ctxt=normal_context) ((loc, expr): Loc.t Ast.Expression.t) = fuse [flat_pretty_space; right]; ]) ] - | E.Member { E.Member._object; property; computed; optional } -> - let ldelim, rdelim = begin match computed, optional with - | false, false -> Atom ".", Empty - | false, true -> Atom "?.", Empty - | true, false -> Atom "[", Atom "]" - | true, true -> Atom "?.[", Atom "]" - end in - fuse [ - begin match _object with - | (_, E.Call _) -> expression ~ctxt _object - | (_, E.Literal { Ast.Literal.value = Ast.Literal.Number num; raw }) when not computed -> - (* 1.foo would be confused with a decimal point, so it needs parens *) - number_literal ~in_member_object:true raw num - | _ -> expression_with_parens ~precedence ~ctxt _object - end; - ldelim; - begin match property with - | E.Member.PropertyIdentifier (loc, id) -> SourceLocation (loc, Atom id) - | E.Member.PropertyPrivateName (loc, (_, id)) -> SourceLocation (loc, Atom ("#" ^id)) - | E.Member.PropertyExpression expr -> expression ~ctxt expr - end; - rdelim; - ] + | E.Member m -> member ~precedence ~ctxt m + | E.OptionalMember { E.OptionalMember.member = m; optional } -> + member ~optional ~precedence ~ctxt m | E.New { E.New.callee; arguments } -> let callee_layout = if definitely_needs_parens ~precedence ctxt callee || @@ -892,6 +847,37 @@ and expression ?(ctxt=normal_context) ((loc, expr): Loc.t Ast.Expression.t) = | E.Generator _ -> not_supported loc "Comprehension not supported" ) +and call ?(optional=false) ~precedence ~ctxt call_node = + let { Ast.Expression.Call.callee; arguments } = call_node in + let lparen = if optional then ".?(" else "(" in + begin match callee, arguments with + (* __d hack, force parens around factory function. + More details at: https://fburl.com/b1wv51vj + TODO: This is FB only, find generic way to add logic *) + | (_, Ast.Expression.Identifier (_, "__d")), [a; b; c; d] -> + fuse [ + Atom "__d"; + list + ~wrap:(Atom lparen, Atom ")") + ~sep:(Atom ",") + [ + expression_or_spread a; + expression_or_spread b; + wrap_in_parens (expression_or_spread c); + expression_or_spread d; + ] + ] + (* Standard call expression printing *) + | _ -> + fuse [ + expression_with_parens ~precedence ~ctxt callee; + list + ~wrap:(Atom lparen, Atom ")") + ~sep:(Atom ",") + (List.map expression_or_spread arguments) + ] + end + and expression_with_parens ~precedence ~(ctxt:expression_context) expr = if definitely_needs_parens ~precedence ctxt expr then wrap_in_parens (expression ~ctxt:normal_context expr) @@ -943,6 +929,35 @@ and literal (loc, { Ast.Literal.raw; value; }) = | _ -> Atom raw ) +and member ?(optional=false) ~precedence ~ctxt member_node = + let { Ast.Expression.Member._object; property; computed } = member_node in + let ldelim, rdelim = begin match computed, optional with + | false, false -> Atom ".", Empty + | false, true -> Atom "?.", Empty + | true, false -> Atom "[", Atom "]" + | true, true -> Atom "?.[", Atom "]" + end in + fuse [ + begin match _object with + | (_, Ast.Expression.Call _) -> expression ~ctxt _object + | (_, Ast.Expression.Literal { Ast.Literal.value = Ast.Literal.Number num; raw }) + when not computed -> + (* 1.foo would be confused with a decimal point, so it needs parens *) + number_literal ~in_member_object:true raw num + | _ -> expression_with_parens ~precedence ~ctxt _object + end; + ldelim; + begin match property with + | Ast.Expression.Member.PropertyIdentifier (loc, id) -> + SourceLocation (loc, Atom id) + | Ast.Expression.Member.PropertyPrivateName (loc, (_, id)) -> + SourceLocation (loc, Atom ("#" ^ id)) + | Ast.Expression.Member.PropertyExpression expr -> + expression ~ctxt expr + end; + rdelim; + ] + and string_literal (loc, { Ast.StringLiteral.value; _ }) = let quote = better_quote value in SourceLocation ( diff --git a/src/parser_utils/ssa_builder.ml b/src/parser_utils/ssa_builder.ml index e32485d258a..77e3782255e 100644 --- a/src/parser_utils/ssa_builder.ml +++ b/src/parser_utils/ssa_builder.ml @@ -949,7 +949,7 @@ class ssa_builder = object(this) method! call _loc (expr: Loc.t Ast.Expression.Call.t) = let open Ast.Expression.Call in - let { callee; arguments; optional = _ } = expr in + let { callee; arguments } = expr in ignore @@ this#expression callee; ignore @@ ListUtils.ident_map this#expression_or_spread arguments; this#havoc_current_ssa_env; diff --git a/src/parsing/parsing_service_js.ml b/src/parsing/parsing_service_js.ml index 87a6daca040..fe7202efecc 100644 --- a/src/parsing/parsing_service_js.ml +++ b/src/parsing/parsing_service_js.ml @@ -153,7 +153,6 @@ let parse_json_file ~fail content file = _object = loc_none, Identifier (loc_none, "module"); property = Member.PropertyIdentifier (loc_none, "exports"); computed = false; - optional = false; }) in let loc = fst expr in let statement = diff --git a/src/typing/destructuring.ml b/src/typing/destructuring.ml index 42112291e8d..4c6d9fac08b 100644 --- a/src/typing/destructuring.ml +++ b/src/typing/destructuring.ml @@ -45,7 +45,6 @@ let destructuring cx ~expr ~f = Ast.Pattern.( } ); computed = true; - optional = false; })) ) in let refinement = Option.bind init (fun init -> @@ -92,7 +91,6 @@ let destructuring cx ~expr ~f = Ast.Pattern.( _object = init; property = PropertyIdentifier (loc, name); computed = false; - optional = false; })) ) in let refinement = Option.bind init (fun init -> @@ -118,7 +116,6 @@ let destructuring cx ~expr ~f = Ast.Pattern.( _object = init; property = PropertyExpression key; computed = true; - optional = false; })) ) in let refinement = Option.bind init (fun init -> diff --git a/src/typing/statement.ml b/src/typing/statement.ml index a6d013d1133..3e1f3880be7 100644 --- a/src/typing/statement.ml +++ b/src/typing/statement.ml @@ -2338,9 +2338,8 @@ and variable cx kind ) in let { VariableDeclaration.Declarator.id; init } = vdecl in Ast.Expression.(match init with - | Some (call_loc, Call { Call.callee = _, Identifier (_, "require"); optional; _ }) + | Some (_, Call { Call.callee = _, Identifier (_, "require"); _ }) when not (Env.local_scope_entry_exists "require") -> - warn_or_ignore_optional_chaining optional cx call_loc; let loc, _ = id in (* Record the loc of the pattern, which contains the locations of any local definitions introduced by the pattern. This information is used @@ -2519,10 +2518,8 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Member { Member._object; property = Member.PropertyExpression index; - optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; let reason = mk_reason (RProperty None) loc in (match Refinement.get cx (loc, e) loc with | Some t -> t @@ -2538,30 +2535,24 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Member { Member._object = _, Identifier (_, "module"); property = Member.PropertyIdentifier (_, "exports"); - optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; get_module_exports cx loc | Member { Member._object = _, Identifier (_, ("ReactGraphQL" | "ReactGraphQLLegacy")); property = Member.PropertyIdentifier (_, "Mixin"); - optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; let reason = mk_reason (RCustom "ReactGraphQLMixin") loc in Flow.get_builtin cx "ReactGraphQLMixin" reason | Member { Member._object = super_loc, Super; property = Member.PropertyIdentifier (ploc, name); - optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; let super = super_ cx super_loc in let id_info = "super", super, Type_table.Other in Env.add_type_table_info cx super_loc id_info; @@ -2591,10 +2582,8 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Member { Member._object; property = Member.PropertyIdentifier (ploc, name); - optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; let tobj = expression cx _object in ( let expr_reason = mk_reason (RProperty (Some name)) loc in @@ -2616,10 +2605,8 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Member { Member._object; property = Member.PropertyPrivateName (ploc, (_, name)); - optional; _ } -> ( - warn_or_ignore_optional_chaining optional cx loc; let expr_reason = mk_reason (RPrivateProperty name) loc in match Refinement.get cx (loc, e) loc with | Some t -> t @@ -2638,6 +2625,10 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e t end + | OptionalMember { OptionalMember.member; optional } -> + warn_or_ignore_optional_chaining optional cx loc; + expression cx (loc, Member member) + | Object { Object.properties } -> let reason = mk_reason RObjectLit loc in object_ cx reason properties @@ -2666,9 +2657,7 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Call { Call.callee = _, Identifier (_, "require"); arguments; - optional; } when not (Env.local_scope_entry_exists "require") -> ( - warn_or_ignore_optional_chaining optional cx loc; match arguments with | [ Expression (source_loc, Ast.Expression.Literal { Ast.Literal.value = Ast.Literal.String module_name; _; @@ -2695,9 +2684,7 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Call { Call.callee = _, Identifier (_, "requireLazy"); arguments; - optional; } when not (Env.local_scope_entry_exists "requireLazy") -> ( - warn_or_ignore_optional_chaining optional cx loc; match arguments with | [Expression(_, Array({Array.elements;})); Expression(callback_expr);] -> (** @@ -2745,6 +2732,10 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e AnyT.at loc ) + | OptionalCall { OptionalCall.call; optional } -> + warn_or_ignore_optional_chaining optional cx loc; + expression cx (loc, Call call) + | New { New.callee = _, Identifier (_, "Function"); arguments @@ -2800,14 +2791,10 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e Call.callee = (callee_loc, Member { Member._object = (_, Identifier (_, "Object") as obj); property = Member.PropertyIdentifier (prop_loc, name); - optional = member_optional; _ } as expr); arguments; - optional; } -> - warn_or_ignore_optional_chaining member_optional cx callee_loc; - warn_or_ignore_optional_chaining optional cx loc; let obj_t = expression cx obj in static_method_call_Object cx loc callee_loc prop_loc expr obj_t name arguments @@ -2815,15 +2802,11 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e Call.callee = (callee_loc, Member { Member._object = super_loc, Super; property = Member.PropertyIdentifier (ploc, name); - optional = member_optional; _ }) as callee; arguments; - optional; _ } -> - warn_or_ignore_optional_chaining member_optional cx callee_loc; - warn_or_ignore_optional_chaining optional cx loc; let reason = mk_reason (RMethodCall (Some name)) loc in let reason_lookup = mk_reason (RProperty (Some name)) callee_loc in let reason_prop = mk_reason (RProperty (Some name)) ploc in @@ -2854,15 +2837,11 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e callee = (lookup_loc, Member { Member. _object; property; - optional = member_optional; _ }) as callee; arguments; - optional; } -> (* method call *) - warn_or_ignore_optional_chaining member_optional cx lookup_loc; - warn_or_ignore_optional_chaining optional cx loc; let ot = expression cx _object in let argts = List.map (expression_or_spread cx) arguments in (match property with @@ -2890,9 +2869,7 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Call { Call.callee = (ploc, Super) as callee; arguments; - optional; } -> - warn_or_ignore_optional_chaining optional cx loc; let argts = List.map (expression_or_spread cx) arguments in let reason = mk_reason (RFunctionCall RSuper) loc in @@ -2921,9 +2898,7 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e | Call { Call.callee = (_, Identifier (_, "invariant")) as callee; arguments; - optional; } -> - warn_or_ignore_optional_chaining optional cx loc; (* TODO: require *) ignore (expression cx callee); (match arguments with @@ -2949,8 +2924,7 @@ and expression_ ~is_cond cx loc e = let ex = (loc, e) in Ast.Expression.(match e ); VoidT.at loc - | Call { Call.callee; arguments; optional } -> - warn_or_ignore_optional_chaining optional cx loc; + | Call { Call.callee; arguments } -> let f = expression cx callee in let reason = mk_reason (RFunctionCall (desc_of_t f)) loc in let argts = @@ -3606,10 +3580,8 @@ and assignment cx loc = Ast.Expression.(function | lhs_loc, Ast.Pattern.Expression (_, Member { Member._object = _, Ast.Expression.Identifier (_, "module"); property = Member.PropertyIdentifier (_, "exports"); - optional; _ }) -> - warn_or_ignore_optional_chaining optional cx lhs_loc; set_module_kind cx lhs_loc (Context.CommonJSModule(Some(lhs_loc))); set_module_exports cx lhs_loc t @@ -3617,10 +3589,8 @@ and assignment cx loc = Ast.Expression.(function | lhs_loc, Ast.Pattern.Expression ((_, Member { Member._object = super_loc, Super; property = Member.PropertyIdentifier (ploc, name); - optional; _ }) as rx) -> - warn_or_ignore_optional_chaining optional cx lhs_loc; let reason = mk_reason (RPropertyAssignment (Some name)) lhs_loc in let prop_reason = mk_reason (RProperty (Some name)) ploc in @@ -3643,10 +3613,8 @@ and assignment cx loc = Ast.Expression.(function | lhs_loc, Ast.Pattern.Expression ((_, Member { Member._object; property = Member.PropertyPrivateName (ploc, (_, name)); - optional; _ }) as expr) -> - warn_or_ignore_optional_chaining optional cx lhs_loc; let o = expression cx _object in (* if we fire this hook, it means the assignment is a sham. *) if not (Type_inference_hooks_js.dispatch_member_hook cx name ploc o) @@ -3674,10 +3642,8 @@ and assignment cx loc = Ast.Expression.(function | lhs_loc, Ast.Pattern.Expression ((_, Member { Member._object; property = Member.PropertyIdentifier (ploc, name); - optional; _ }) as expr) -> - warn_or_ignore_optional_chaining optional cx lhs_loc; let o = expression cx _object in let wr_ctx = match _object, Env.var_scope_kind () with | (_, This), Scope.Ctor -> ThisInCtor @@ -3708,10 +3674,8 @@ and assignment cx loc = Ast.Expression.(function | lhs_loc, Ast.Pattern.Expression ((_, Member { Member._object; property = Member.PropertyExpression ((iloc, _) as index); - optional; _ }) as rx) -> - warn_or_ignore_optional_chaining optional cx lhs_loc; let reason = mk_reason (RPropertyAssignment None) lhs_loc in let a = expression cx _object in let i = expression cx index in @@ -4081,13 +4045,11 @@ and jsx_desugar cx name component_t props attributes children locs = component = reason_of_t component_t; }) in Ast.Expression.(match jsx_expr with - | jsx_loc, Member { + | _, Member { Member._object; property = Member.PropertyIdentifier (prop_loc, name); - optional; _; } -> - warn_or_ignore_optional_chaining optional cx jsx_loc; let ot = jsx_pragma_expression cx raw_jsx_expr loc_element _object in method_call cx reason ~use_op ~call_strict_arity:false prop_loc (jsx_expr, ot, name) argts @@ -4173,7 +4135,6 @@ and jsx_title_member_to_expression member = _object; property = PropertyIdentifier property; computed = false; - optional = false; }) ) @@ -4236,11 +4197,8 @@ and predicates_of_condition cx e = Ast.(Expression.( (expr_loc, Member { Member._object; property = Member.PropertyIdentifier (prop_loc, prop_name); - optional; _ }) -> - warn_or_ignore_optional_chaining optional cx expr_loc; - (* use `expression` instead of `condition` because `_object` is the object in a member expression; if it itself is a member expression, it must exist (so ~is_cond:false). e.g. `foo.bar.baz` shows up here as @@ -4510,11 +4468,8 @@ and predicates_of_condition cx e = Ast.(Expression.( | loc, Member { Member._object; property = Member.PropertyIdentifier (prop_loc, prop_name); - optional; - _ - } - -> - warn_or_ignore_optional_chaining optional cx loc; + _ + } -> let obj_t = match _object with | super_loc, Super -> let t = super_ cx super_loc in @@ -4598,14 +4553,9 @@ and predicates_of_condition cx e = Ast.(Expression.( Call.callee = callee_loc, Member { Member._object = (_, Identifier (_, "Array") as o); property = Member.PropertyIdentifier (prop_loc, "isArray"); - optional = member_optional; _ }; arguments = [Expression arg]; - optional; } -> ( - warn_or_ignore_optional_chaining member_optional cx callee_loc; - warn_or_ignore_optional_chaining optional cx loc; - (* get Array.isArray in order to populate the type tables, but we don't care about the result. *) (* TODO: one day we can replace this with a call to `method_call`, and @@ -4679,17 +4629,14 @@ and predicates_of_condition cx e = Ast.(Expression.( (* e.m(...) *) (* TODO: Don't trap method calls for now *) - | loc, Call { Call.callee = (_, Member _); optional; _ } -> - warn_or_ignore_optional_chaining optional cx loc; + | _, Call { Call.callee = (_, Member _); _ } -> empty_result (expression cx e) (* f(...) *) (* The concrete predicate is not known at this point. We attach a "latent" predicate pointing to the type of the function that will supply this predicated when it is resolved. *) - | loc, Call { Call.callee = c; arguments; optional } - -> - warn_or_ignore_optional_chaining optional cx loc; + | loc, Call { Call.callee = c; arguments } -> let is_spread = function | Spread _ -> true | _ -> false in if List.exists is_spread arguments then empty_result (expression cx e) diff --git a/testgen/ruleset_base.ml b/testgen/ruleset_base.ml index 84679607d72..518cd5b1ac5 100644 --- a/testgen/ruleset_base.ml +++ b/testgen/ruleset_base.ml @@ -778,8 +778,7 @@ class ruleset_base = object(self) let open E.Member in E.Member {_object = (Loc.none, obj); property = PropertyExpression (Loc.none, prop); - computed = false; - optional = false} in + computed = false} in let rec get_prop (oname : Loc.t E.t') (ot : Loc.t T.Object.t) (depth : int) : env_elt_t = let prop = self#choose depth (fun () -> self#require_prop (T.Object ot) true) in @@ -821,8 +820,7 @@ class ruleset_base = object(self) let open E.Member in E.Member {_object = (Loc.none, obj); property = PropertyExpression (Loc.none, prop); - computed = false; - optional = false} in + computed = false} in let rec get_prop (oname : Loc.t E.t') (ot : Loc.t T.Object.t) (depth : int) : env_elt_t = let prop = self#choose depth (fun () -> self#require_optional_prop (T.Object ot)) in diff --git a/testgen/ruleset_func.ml b/testgen/ruleset_func.ml index f0f01376cb0..4f04d3200ca 100644 --- a/testgen/ruleset_func.ml +++ b/testgen/ruleset_func.ml @@ -97,13 +97,11 @@ class ruleset_func = object(self) List.fold_right (fun elt acc -> match elt with | Expr (E.Call {callee = _, fid; - arguments = args; - optional}, _) -> + arguments = args}, _) -> let ftype = self#get_type_from_expr fid env in if self#is_subtype param_type ftype then begin (Expr (E.Call {callee = (Loc.none, E.Identifier (Loc.none, pname)); - arguments = args; - optional}, rt)) :: elt :: acc + arguments = args}, rt)) :: elt :: acc end else elt :: acc | _ -> elt :: acc) env [] (* If the parameter is an object, we create new property read *) @@ -113,14 +111,12 @@ class ruleset_func = object(self) match elt with | Expr (E.Member {_object = _, obj; property = prop; - computed = c; - optional}, t) -> + computed = c}, t) -> let otype = self#get_type_from_expr obj env in if self#is_subtype param_type otype then begin (Expr (E.Member {_object = (Loc.none, E.Identifier (Loc.none, pname)); property = prop; - computed = c; - optional}, t)) :: elt :: acc + computed = c}, t)) :: elt :: acc end else elt :: acc | _ -> elt :: acc) env [] | _ -> env) in diff --git a/testgen/syntax_base.ml b/testgen/syntax_base.ml index eecbfe30b78..257c5fc4377 100644 --- a/testgen/syntax_base.ml +++ b/testgen/syntax_base.ml @@ -93,7 +93,7 @@ let mk_runtime_check (expr : Loc.t E.t') (etype : Loc.t T.t') : t = [E.Expression (Loc.none, expr); E.Expression (Loc.none, (mk_literal_expr etype))] in let call = let open E.Call in - E.Call {callee = (Loc.none, callee); arguments; optional = false} in + E.Call {callee = (Loc.none, callee); arguments} in Stmt (S.Expression.(S.Expression {expression = (Loc.none, call); directive = None})) @@ -107,8 +107,7 @@ let mk_check_opt_prop (expr : Loc.t E.t') (etype : Loc.t T.t') : t = match read with | E.Member {_object = (_, obj); property = _; - computed = _; - optional = _} -> get_obj obj (obj :: acc) + computed = _} -> get_obj obj (obj :: acc) | _ -> List.rev acc in (* We want to make sure the parent is not undefined *) @@ -122,7 +121,7 @@ let mk_check_opt_prop (expr : Loc.t E.t') (etype : Loc.t T.t') : t = E.Expression (Loc.none, expr); E.Expression (Loc.none, (mk_literal_expr etype))] in let call = let open E.Call in - E.Call {callee = (Loc.none, callee); arguments; optional = false} in + E.Call {callee = (Loc.none, callee); arguments} in Stmt (S.Expression.(S.Expression {expression = (Loc.none, call); directive = None})) @@ -173,8 +172,7 @@ let mk_func_def let mk_func_call (fid : Loc.t E.t') (param : Loc.t E.t') : t = Expr (E.Call.(E.Call {callee = (Loc.none, fid); - arguments = [E.Expression (Loc.none, param)]; - optional = false})) + arguments = [E.Expression (Loc.none, param)]})) let mk_literal (t : Loc.t T.t') : t = match t with | T.Number -> @@ -194,8 +192,7 @@ let mk_prop_read let open E.Member in Expr (E.Member {_object = (Loc.none, E.Identifier (Loc.none, obj_name)); property = PropertyIdentifier (Loc.none, prop_name); - computed = false; - optional = false}) + computed = false}) let mk_prop_write (oname : string)