From 7e5b2a9a4bed89d97b5961b27d92a405f98f7b65 Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Thu, 16 Jul 2015 17:03:19 -0700 Subject: [PATCH] [Parser] Update ast-types and fix up parser Summary: There were a few changes made to ast-types and the parser was no longer passing the tests. This diff fixes those various issues. The main issues were: 1. Parser support for class property initializers. Flow still doesn't have proper support for them, but they should at least parse. 2. Class method kinds are now `"constructor", "method", "get" and "set"` instead of `"", "init", "get", "set"`. This matches what babel and esprima are doing, though esprima-fb is still stuck with the old kinds. Reviewed By: @avikchaudhuri Differential Revision: D2252999 --- src/parser/estree_translator.ml | 29 +++-- src/parser/package.json | 2 +- src/parser/parser_flow.ml | 52 +++++--- src/parser/spider_monkey_ast.ml | 10 +- src/parser/test/esprima_ast_types.js | 20 +++ src/parser/test/esprima_test_runner.js | 19 ++- src/parser/test/hardcoded_test_runner.js | 2 +- src/parser/test/hardcoded_tests.js | 119 +++++++++++++++++- src/typing/comments_js.ml | 2 +- src/typing/type_inference_js.ml | 21 ++-- tests/class_property_initializers/.flowconfig | 7 ++ .../class_property_initializers.exp | 8 ++ .../class_property_initializers.js | 9 ++ 13 files changed, 253 insertions(+), 47 deletions(-) create mode 100644 src/parser/test/esprima_ast_types.js create mode 100644 tests/class_property_initializers/.flowconfig create mode 100644 tests/class_property_initializers/class_property_initializers.exp create mode 100644 tests/class_property_initializers/class_property_initializers.js diff --git a/src/parser/estree_translator.ml b/src/parser/estree_translator.ml index c189f63cd4f..3a519127749 100644 --- a/src/parser/estree_translator.ml +++ b/src/parser/estree_translator.ml @@ -613,24 +613,22 @@ end with type t = Impl.t) = struct and class_method (loc, method_) = let { Class.Method.key; value; kind; static; } = method_ in - Expression.Object.Property.( - let key, computed = (match key with + let key, computed = Expression.Object.Property.(match key with | Literal lit -> literal lit, false | Identifier id -> identifier id, false | Computed expr -> expression expr, true) in - let kind = match kind with - | Init -> "init" + let kind = Class.Method.(match kind with + | Constructor -> "constructor" + | Method -> "method" | Get -> "get" - | Set -> "set" - in - node "MethodDefinition" loc [| - "key", key; - "value", function_expression value; - "kind", string kind; - "static", bool static; - "computed", bool computed; - |] - ) + | Set -> "set") in + node "MethodDefinition" loc [| + "key", key; + "value", function_expression value; + "kind", string kind; + "static", bool static; + "computed", bool computed; + |] and class_property (loc, prop) = Class.Property.( let key, computed = (match prop.key with @@ -639,7 +637,8 @@ end with type t = Impl.t) = struct | Expression.Object.Property.Computed expr -> expression expr, true) in node "ClassProperty" loc [| "key", key; - "typeAnnotation", type_annotation prop.typeAnnotation; + "value", option expression prop.value; + "typeAnnotation", option type_annotation prop.typeAnnotation; "computed", bool computed; "static", bool prop.static; |] diff --git a/src/parser/package.json b/src/parser/package.json index 03d015d4953..e22829367ce 100644 --- a/src/parser/package.json +++ b/src/parser/package.json @@ -18,7 +18,7 @@ "esprima-fb": "15001.1.0-dev-harmony-fb" }, "dependencies": { - "ast-types": "0.7.8", + "ast-types": "0.8.4", "colors": ">=0.6.2", "minimist": ">=0.2.0" }, diff --git a/src/parser/parser_flow.ml b/src/parser/parser_flow.ml index 4bf398aa5c7..d7a42afbd1b 100644 --- a/src/parser/parser_flow.ml +++ b/src/parser/parser_flow.ml @@ -214,10 +214,10 @@ end = struct val type_parameter_declaration : env -> Ast.Type.ParameterDeclaration.t option val type_parameter_instantiation : env -> Ast.Type.ParameterInstantiation.t option val generic : env -> Loc.t * Ast.Type.Generic.t - val return_type : env -> Ast.Type.annotation option val _object : ?allow_static:bool -> env -> Loc.t * Type.Object.t val function_param_list : env -> Type.Function.Param.t option * Type.Function.Param.t list val annotation : env -> Ast.Type.annotation + val annotation_opt : env -> Ast.Type.annotation option end = struct type param_list_or_type = | ParamList of (Type.Function.Param.t option * Type.Function.Param.t list) @@ -723,7 +723,7 @@ end = struct let loc, generic = raw_generic_with_identifier env id in loc, Type.Generic generic - and return_type env = + and annotation_opt env = match Peek.token env with | T_COLON -> Some (annotation env) | _ -> None @@ -738,10 +738,10 @@ end = struct let _type = wrap _type let type_parameter_declaration = wrap type_parameter_declaration let type_parameter_instantiation = wrap type_parameter_instantiation - let return_type = wrap return_type let _object ?(allow_static=false) env = wrap (_object ~allow_static) env let function_param_list = wrap function_param_list let annotation = wrap annotation + let annotation_opt = wrap annotation_opt let generic = wrap generic end @@ -920,7 +920,7 @@ end = struct ) in let typeParameters = Type.type_parameter_declaration env in let params, defaults, rest = function_params env in - let returnType = Type.return_type env in + let returnType = Type.annotation_opt env in let _, body, strict = function_body env ~async ~generator in let simple = is_simple_function_params params defaults rest in strict_post_check env ~strict ~simple id params; @@ -1530,7 +1530,7 @@ end = struct id, Type.type_parameter_declaration env end in let params, defaults, rest = Declaration.function_params env in - let returnType = Type.return_type env in + let returnType = Type.annotation_opt env in let end_loc, body, strict = Declaration.function_body env ~async ~generator in let simple = Declaration.is_simple_function_params params defaults rest in @@ -1774,7 +1774,7 @@ end = struct [param], [], None, None else let params, defaults, rest = Declaration.function_params env in - params, defaults, rest, Type.return_type env in + params, defaults, rest, Type.annotation_opt env in (* It's hard to tell if an invalid expression was intended to be an * arrow function before we see the =>. If there are no params, that @@ -1957,7 +1957,7 @@ end = struct [ (fst param, Pattern.Identifier param) ] | Init -> assert false) in Expect.token env T_RPAREN; - let returnType = Type.return_type env in + let returnType = Type.annotation_opt env in let _, body, strict = Declaration.function_body env ~async ~generator in let defaults = [] in let rest = None in @@ -2053,7 +2053,7 @@ end = struct | T_LPAREN -> let typeParameters = Type.type_parameter_declaration env in let params, defaults, rest = Declaration.function_params env in - let returnType = Type.return_type env in + let returnType = Type.annotation_opt env in let _, body, strict = Declaration.function_body env ~async ~generator in let simple = Declaration.is_simple_function_params params defaults rest in @@ -2209,7 +2209,7 @@ end = struct Ast.Class.(Body.Method (Loc.btwn start_loc end_loc, Method.({ key; value; - kind = Ast.Expression.Object.Property.Get; + kind = Get; static; }))) @@ -2219,27 +2219,34 @@ end = struct Ast.Class.(Body.Method (Loc.btwn start_loc end_loc, Method.({ key; value; - kind = Ast.Expression.Object.Property.Set; + kind = Set; static; }))) in let init env start_loc key async generator static = - if not async && not generator && Peek.token env = T_COLON - then begin + match Peek.token env with + | T_COLON + | T_ASSIGN + | T_SEMICOLON when not async && not generator -> (* Class property with annotation *) - let typeAnnotation = Type.annotation env in + let typeAnnotation = Type.annotation_opt env in + let value = + if Expect.maybe env T_ASSIGN + then Some (Parse.expression env) + else None in let end_loc = Peek.loc env in Expect.token env T_SEMICOLON; let loc = Loc.btwn start_loc end_loc in Ast.Class.(Body.Property (loc, Property.({ key; + value; typeAnnotation; static; }))) - end else begin + | _ -> let typeParameters = Type.type_parameter_declaration env in let params, defaults, rest = Declaration.function_params env in - let returnType = Type.return_type env in + let returnType = Type.annotation_opt env in let _, body, strict = Declaration.function_body env ~async ~generator in let simple = @@ -2261,13 +2268,24 @@ end = struct returnType; typeParameters; }) in + let kind = Ast.(match key with + | Expression.Object.Property.Identifier (_, { + Identifier.name = "constructor"; + _; + }) + | Expression.Object.Property.Literal (_, { + Literal.value = Literal.String "constructor"; + _; + }) -> + Class.Method.Constructor + | _ -> + Class.Method.Method) in Ast.Class.(Body.Method (Loc.btwn start_loc end_loc, Method.({ key; value; - kind = Ast.Expression.Object.Property.Init; + kind; static; }))) - end in fun env -> Ast.Expression.Object.Property.( let start_loc = Peek.loc env in diff --git a/src/parser/spider_monkey_ast.ml b/src/parser/spider_monkey_ast.ml index b109edcb121..672978b15ad 100644 --- a/src/parser/spider_monkey_ast.ml +++ b/src/parser/spider_monkey_ast.ml @@ -906,8 +906,13 @@ end = Comment and Class : sig module Method : sig type t = Loc.t * t' + and kind = + | Constructor + | Method + | Get + | Set and t' = { - kind: Expression.Object.Property.kind; + kind: kind; key: Expression.Object.Property.key; value: Loc.t * Expression.Function.t; static: bool; @@ -917,7 +922,8 @@ and Class : sig type t = Loc.t * t' and t' = { key: Expression.Object.Property.key; - typeAnnotation: Type.annotation; + value: Expression.t option; + typeAnnotation: Type.annotation option; static: bool; } end diff --git a/src/parser/test/esprima_ast_types.js b/src/parser/test/esprima_ast_types.js new file mode 100644 index 00000000000..c6387ca0d28 --- /dev/null +++ b/src/parser/test/esprima_ast_types.js @@ -0,0 +1,20 @@ +/* This is basically the ast-types main.js, except without the conflicting + * babel definitions */ +var types = require("ast-types/lib/types"); + +// This core module of AST types captures ES5 as it is parsed today by +// git://github.com/ariya/esprima.git#master. +require("ast-types/def/core"); + +// Feel free to add to or remove from this list of extension modules to +// configure the precise type hierarchy that you need. +require("ast-types/def/es6"); +require("ast-types/def/es7"); +require("ast-types/def/mozilla"); +require("ast-types/def/e4x"); +require("ast-types/def/fb-harmony"); +require("ast-types/def/esprima"); + +types.finalize(); + +exports.namedTypes = types.namedTypes; diff --git a/src/parser/test/esprima_test_runner.js b/src/parser/test/esprima_test_runner.js index 778635053c0..d446a86dfbd 100644 --- a/src/parser/test/esprima_test_runner.js +++ b/src/parser/test/esprima_test_runner.js @@ -5,7 +5,7 @@ var esprima = require("esprima-fb"); var flow = require("../flow_parser.js"); var util = require("util"); -var ast_types = require("ast-types"); +var ast_types = require("./esprima_ast_types.js"); function new_env() { var diffs = {}; @@ -183,6 +183,20 @@ function handleSpecialObjectCompare(esprima, flow, env) { esprima.implements = []; } break; + case "ClassBody": + // esprima-fb is pretty out of date here. The 4 kinds should be + // "constructor", "method", "get" and "set" + for (var i = 0; i < esprima.body.length; i++) { + var body = esprima.body[i]; + if (body && body.type == "MethodDefinition") { + if (body.key.name === "constructor") { + body.kind = "constructor"; + } else if (body.kind === "init" || body.kind === "") { + body.kind = "method"; + } + } + } + break; case 'ArrayPattern': // Esprima has the wrong node type for spread elements in an array pattern for (var i = 0; i < esprima.elements.length; i++) { @@ -197,8 +211,11 @@ function handleSpecialObjectCompare(esprima, flow, env) { case 'ObjectTypeProperty': case 'ObjectTypeIndexer': case 'ObjectTypeCallProperty': + esprima.static = esprima.static || false; + break; case 'ClassProperty': esprima.static = esprima.static || false; + esprima.value = null; break; case 'ArrowFunctionExpression': esprima.returnType = null; diff --git a/src/parser/test/hardcoded_test_runner.js b/src/parser/test/hardcoded_test_runner.js index b6d5a0158ea..704add1d333 100644 --- a/src/parser/test/hardcoded_test_runner.js +++ b/src/parser/test/hardcoded_test_runner.js @@ -1,6 +1,6 @@ var flow = require("./../flow_parser.js"); var util = require("util"); -var ast_types = require("ast-types"); +var ast_types = require("./esprima_ast_types.js"); function new_env() { var diffs = {}; diff --git a/src/parser/test/hardcoded_tests.js b/src/parser/test/hardcoded_tests.js index 85ddd9372b0..afc7767dccd 100644 --- a/src/parser/test/hardcoded_tests.js +++ b/src/parser/test/hardcoded_tests.js @@ -2182,5 +2182,122 @@ module.exports = { 'var x = async\ny => y': { 'body.length': 2, }, - } + }, + 'Class Method Kinds': { + 'class Kind { foo() {} }': { + 'body.0.body.body.0.kind': 'method', + }, + 'class Kind { "foo"() {} }': { + 'body.0.body.body.0.kind': 'method', + }, + 'class Kind { constructor() {} }': { + 'body.0.body.body.0.kind': 'constructor', + }, + 'class Kind { "constructor"() {} }': { + 'body.0.body.body.0.kind': 'constructor', + }, + 'class Kind { get a() {} }': { + 'body.0.body.body.0.kind': 'get', + }, + 'class Kind { get "a"() {} }': { + 'body.0.body.body.0.kind': 'get', + }, + 'class Kind { set a(x) {} }': { + 'body.0.body.body.0.kind': 'set', + }, + 'class Kind { set "a"(x) {} }': { + 'body.0.body.body.0.kind': 'set', + }, + }, + 'Class Properties': { + 'class Properties { x; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation': null, + 'value': null, + 'static': false, + 'computed': false, + }] + }, + 'class Properties { x: string; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation.typeAnnotation.type': 'StringTypeAnnotation', + 'value': null, + 'static': false, + 'computed': false, + }] + }, + 'class Properties { x = "hello"; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation': null, + 'value.value': "hello", + 'static': false, + 'computed': false, + }] + }, + 'class Properties { x: string = "hello"; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation.typeAnnotation.type': 'StringTypeAnnotation', + 'value.value': "hello", + 'static': false, + 'computed': false, + }] + }, + 'class Properties { static x; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation': null, + 'value': null, + 'static': true, + 'computed': false, + }] + }, + 'class Properties { static x: string; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation.typeAnnotation.type': 'StringTypeAnnotation', + 'value': null, + 'static': true, + 'computed': false, + }] + }, + 'class Properties { static x = "hello"; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation': null, + 'value.value': "hello", + 'static': true, + 'computed': false, + }] + }, + 'class Properties { static x: string = "hello"; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'key.name': 'x', + 'typeAnnotation.typeAnnotation.type': 'StringTypeAnnotation', + 'value.value': "hello", + 'static': true, + 'computed': false, + }] + }, + 'class Properties { static [x]: string = "hello"; }': { + 'body.0.body.body': [{ + 'type': 'ClassProperty', + 'typeAnnotation.typeAnnotation.type': 'StringTypeAnnotation', + 'value.value': "hello", + 'static': true, + 'computed': true, + }] + }, + }, }; diff --git a/src/typing/comments_js.ml b/src/typing/comments_js.ml index 0c91c614f8b..6ce1cad2f09 100644 --- a/src/typing/comments_js.ml +++ b/src/typing/comments_js.ml @@ -296,7 +296,7 @@ and meta_statement cmap = Ast.Statement.(function Method.key = Ast.Expression.Object.Property.Identifier (_, { Ast.Identifier.name; _ }); value = _, { Ast.Expression.Function.params; body; _ }; - kind = Ast.Expression.Object.Property.Init; + kind = Method.Method | Method.Constructor; static = false }) -> meta_fbody cmap loc params body diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 4b881b6aa2c..fd38981ba79 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -4781,7 +4781,7 @@ and mk_signature cx reason_c c_type_params_map superClass body = Ast.Class.( { Ast.Identifier.name; _ }); value = _, { Ast.Expression.Function.params; defaults; rest; returnType; typeParameters; body; _ }; - kind = Ast.Expression.Object.Property.Init; + kind = Method.Method | Method.Constructor; static; }) -> @@ -4810,10 +4810,17 @@ and mk_signature cx reason_c c_type_params_map superClass body = Ast.Class.( | Body.Property (loc, { Property.key = Ast.Expression.Object.Property.Identifier (_, { Ast.Identifier.name; _ }); - typeAnnotation = (_, typeAnnotation); + typeAnnotation; + value; static; }) -> - let t = convert cx c_type_params_map typeAnnotation in + if value <> None + then begin + let msg = "class property initializers are not yet supported" in + Flow_js.add_error cx [mk_reason "" loc, msg] + end; + let r = mk_reason (spf "class property %s" name) loc in + let t = mk_type_annotation_ cx c_type_params_map r typeAnnotation in if static then SMap.add name t sfields, @@ -4828,8 +4835,8 @@ and mk_signature cx reason_c c_type_params_map superClass body = Ast.Class.( (* get/set *) | Body.Method (loc, { - Method.kind = Ast.Expression.Object.Property.Get - | Ast.Expression.Object.Property.Set; + Method.kind = Method.Get + | Method.Set; _ }) -> let msg = "get/set properties not yet supported" in @@ -4839,7 +4846,6 @@ and mk_signature cx reason_c c_type_params_map superClass body = Ast.Class.( (* literal LHS *) | Body.Method (loc, { Method.key = Ast.Expression.Object.Property.Literal _; - Method.kind = Ast.Expression.Object.Property.Init; _ }) | Body.Property (loc, { @@ -4853,7 +4859,6 @@ and mk_signature cx reason_c c_type_params_map superClass body = Ast.Class.( (* computed LHS *) | Body.Method (loc, { Method.key = Ast.Expression.Object.Property.Computed _; - Method.kind = Ast.Expression.Object.Property.Init; _ }) | Body.Property (loc, { @@ -4876,7 +4881,7 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( { Ast.Identifier.name; _ }); value = _, { Ast.Expression.Function.params; defaults; rest; returnType; typeParameters; body; async; _ }; - kind = Ast.Expression.Object.Property.Init; + kind = Method.Method | Method.Constructor; static; }) -> let this, super, method_sigs = diff --git a/tests/class_property_initializers/.flowconfig b/tests/class_property_initializers/.flowconfig new file mode 100644 index 00000000000..4a58bdcdef3 --- /dev/null +++ b/tests/class_property_initializers/.flowconfig @@ -0,0 +1,7 @@ +[ignore] + +[include] + +[libs] + +[options] diff --git a/tests/class_property_initializers/class_property_initializers.exp b/tests/class_property_initializers/class_property_initializers.exp new file mode 100644 index 00000000000..14295f4f814 --- /dev/null +++ b/tests/class_property_initializers/class_property_initializers.exp @@ -0,0 +1,8 @@ + +class_property_initializers.js:7:3,30: +class property initializers are not yet supported + +class_property_initializers.js:8:3,44: +class property initializers are not yet supported + +Found 2 errors diff --git a/tests/class_property_initializers/class_property_initializers.js b/tests/class_property_initializers/class_property_initializers.js new file mode 100644 index 00000000000..761479a8e57 --- /dev/null +++ b/tests/class_property_initializers/class_property_initializers.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +class A { + thisIsSupported: string; + thisIsNotSupportedYet = 123; + thisIsNotSupportedYetEither: number = 123; +}