Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for abstract classes #4005

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d74a3e3
Add abstract parsing for instance and class methods
popham May 22, 2017
12a3f3e
Plumb in the data structures for abstractness support
popham May 22, 2017
fb25c24
Set up the insttype.abstracts with GatherAbstractT
popham May 22, 2017
955e280
Set up nonabstract assertion handling
popham May 22, 2017
2e84b52
Block abstract classes from ConstructorT and touching statics
popham May 23, 2017
e7930b8
Repair broken tests
popham May 23, 2017
8338a92
Vacuous toplevels for converted function types
popham May 23, 2017
e049dd8
Bug fix
popham May 23, 2017
8e00ca1
Label abstract methods as such in error messages
popham May 23, 2017
cf7ad65
Remove misguided reservation
popham May 23, 2017
7b2e4ee
Remove no-op reposition
popham May 23, 2017
db7b6b2
Mixin facebookism rejects abstract classes cleanly
popham May 23, 2017
928cd61
Remove misguided reservation
popham May 23, 2017
5679404
Allow setting of static fields on abstract classes
popham May 23, 2017
0940c5c
Formalize personal TODOs for review
popham May 23, 2017
028f8a2
Punt on class property initializer safety
popham May 23, 2017
3cce5f8
Under UnimplementedError, order list of abstracts by loc
popham May 23, 2017
d63d9c8
OCD on whitespace
popham May 23, 2017
919c140
Allow lookups without abstractness checking
popham May 23, 2017
af0f834
Use AbstractClass<.> to move abstracts without abstractness checking
popham May 24, 2017
bc065f1
Verify that `this` is still working properly
popham May 24, 2017
1a95134
Include new types
popham May 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/common/reason.ml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type reason_desc =
| RTupleOutOfBoundsAccess
| RFunction of reason_desc_function
| RFunctionType
| RAbstractMethodType
| RFunctionBody
| RFunctionCall
| RFunctionUnusedArgument
Expand Down Expand Up @@ -146,6 +147,8 @@ type reason_desc =
| RMaybe of reason_desc
| RRestArray of reason_desc
| RAbstract of reason_desc
| RAbstracts of reason_desc
| RDummyAbstracts
| RTypeApp of reason_desc
| RThisTypeApp of reason_desc
| RExtends of reason_desc
Expand Down Expand Up @@ -374,6 +377,7 @@ let rec string_of_desc = function
| RTupleOutOfBoundsAccess -> "undefined (out of bounds tuple access)"
| RFunction func -> spf "%sfunction" (function_desc_prefix func)
| RFunctionType -> "function type"
| RAbstractMethodType -> "abstract method type"
| RFunctionBody -> "function body"
| RFunctionCall -> "function call"
| RFunctionUnusedArgument -> "unused function argument"
Expand Down Expand Up @@ -453,6 +457,8 @@ let rec string_of_desc = function
| RMaybe d -> spf "?%s" (string_of_desc d)
| RRestArray d -> spf "rest parameter array of %s" (string_of_desc d)
| RAbstract d -> spf "abstract %s" (string_of_desc d)
| RAbstracts d -> spf "abstracts of %s" (string_of_desc d)
| RDummyAbstracts -> "vacuous abstracts"
| RTypeApp d -> spf "type application of %s" (string_of_desc d)
| RThisTypeApp d -> spf "this instantiation of %s" (string_of_desc d)
| RExtends d -> spf "extends %s" (string_of_desc d)
Expand Down
3 changes: 3 additions & 0 deletions src/common/reason.mli
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type reason_desc =
| RTupleOutOfBoundsAccess
| RFunction of reason_desc_function
| RFunctionType
| RAbstractMethodType
| RFunctionBody
| RFunctionCall
| RFunctionUnusedArgument
Expand Down Expand Up @@ -98,6 +99,8 @@ type reason_desc =
| RMaybe of reason_desc
| RRestArray of reason_desc
| RAbstract of reason_desc
| RAbstracts of reason_desc
| RDummyAbstracts
| RTypeApp of reason_desc
| RThisTypeApp of reason_desc
| RExtends of reason_desc
Expand Down
9 changes: 9 additions & 0 deletions src/parser/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,14 @@ and Comment : sig
end = Comment

and Class : sig
module AbstractMethod : sig
type t = Loc.t * t'
and t' = {
key: Expression.Object.Property.key;
value: Loc.t * Type.Function.t;
static: bool;
}
end
module Method : sig
type t = Loc.t * t'
and kind =
Expand Down Expand Up @@ -1002,6 +1010,7 @@ and Class : sig
end
module Body : sig
type element =
| AbstractMethod of AbstractMethod.t
| Method of Method.t
| Property of Property.t
type t = Loc.t * t'
Expand Down
14 changes: 14 additions & 0 deletions src/parser/estree_translator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -706,9 +706,23 @@ end with type t = Impl.t) = struct
)

and class_element = Class.Body.(function
| AbstractMethod m -> class_abstract_method m
| Method m -> class_method m
| Property p -> class_property p)

and class_abstract_method (loc, method_) =
let { Class.AbstractMethod.key; value; static; } = method_ in
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
node "AbstractMethodDefinition" loc [|
"key", key;
"value", function_type value;
"static", bool static;
"computed", bool computed;
|]

and class_method (loc, method_) =
let { Class.Method.key; value; kind; static; decorators; } = method_ in
let key, computed = Expression.Object.Property.(match key with
Expand Down
28 changes: 15 additions & 13 deletions src/parser/lexer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ let _ = List.iter (fun (key, token) -> Hashtbl.add keywords key token)
"for", T_FOR;
"class", T_CLASS;
"extends", T_EXTENDS;
"abstract", T_ABSTRACT;
"static", T_STATIC;
"else", T_ELSE;
"new", T_NEW;
Expand Down Expand Up @@ -394,19 +395,20 @@ let _ = List.iter (fun (key, token) -> Hashtbl.add keywords key token)
]
let _ = List.iter (fun (key, token) -> Hashtbl.add type_keywords key token)
[
"static", T_STATIC;
"typeof", T_TYPEOF;
"any", T_ANY_TYPE;
"mixed", T_MIXED_TYPE;
"empty", T_EMPTY_TYPE;
"bool", T_BOOLEAN_TYPE;
"boolean", T_BOOLEAN_TYPE;
"true", T_TRUE;
"false", T_FALSE;
"number", T_NUMBER_TYPE;
"string", T_STRING_TYPE;
"void", T_VOID_TYPE;
"null", T_NULL;
"abstract", T_ABSTRACT;
"static", T_STATIC;
"typeof", T_TYPEOF;
"any", T_ANY_TYPE;
"mixed", T_MIXED_TYPE;
"empty", T_EMPTY_TYPE;
"bool", T_BOOLEAN_TYPE;
"boolean", T_BOOLEAN_TYPE;
"true", T_TRUE;
"false", T_FALSE;
"number", T_NUMBER_TYPE;
"string", T_STRING_TYPE;
"void", T_VOID_TYPE;
"null", T_NULL;
]

let rec token (env: Lex_env.t) lexbuf =
Expand Down
151 changes: 105 additions & 46 deletions src/parser/object_parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -332,16 +332,61 @@ module Object
decorators;
})))

in let method_value env async generator =
let func_loc = Peek.loc env in
let typeParameters = Type.type_parameter_declaration env in
let params = Declaration.function_params 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 in
Declaration.strict_post_check env ~strict ~simple None params;
let end_loc, expression = Function.(
match body with
| BodyBlock (loc, _) -> loc, false
| BodyExpression (loc, _) -> loc, true) in
Loc.btwn func_loc end_loc, Function.({
id = None;
params;
body;
generator;
async;
(* TODO: add support for method predicates *)
predicate = None;
expression;
returnType;
typeParameters;
})

in let abstract_method_value env =
let func_loc = Peek.loc env in
let typeParameters = Type.type_parameter_declaration env in
let params = Type.function_param_list env in
Expect.token env T_COLON;
let returnType = Type._type env in
let end_loc = fst returnType in
Expect.token env T_SEMICOLON;
Loc.btwn func_loc end_loc, Ast.Type.Function.({
params;
returnType;
typeParameters;
})

in let error_unsupported_variance env = function
| Some (loc, _) -> error_at env (loc, Error.UnexpectedVariance)
| None -> ()

in let rec init env start_loc decorators key async generator static variance =
in let error_unsupported_abstract env = function
| Some loc -> error_at env (loc, Error.UnexpectedAbstract)
| None -> ()

in let rec init env start_loc decorators key async generator abstract static variance =
match Peek.token env with
| T_COLON
| T_ASSIGN
| T_SEMICOLON when not async && not generator ->
(* Class property with annotation *)
error_unsupported_abstract env abstract;
let end_loc, (typeAnnotation, value) = with_loc (fun env ->
let typeAnnotation = Type.annotation_opt env in
let options = parse_options env in
Expand Down Expand Up @@ -374,54 +419,66 @@ module Object
(* TODO: add support for optional class properties *)
error_unexpected env;
Eat.token env;
init env start_loc decorators key async generator static variance
init env start_loc decorators key async generator abstract static variance
| _ ->
error_unsupported_variance env variance;
let func_loc = Peek.loc env in
let typeParameters = Type.type_parameter_declaration env in
let params = Declaration.function_params 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 in
Declaration.strict_post_check env ~strict ~simple None params;
let end_loc, expression = Function.(
match body with
| BodyBlock (loc, _) -> loc, false
| BodyExpression (loc, _) -> loc, true) in
let loc = Loc.btwn func_loc end_loc in
let value = loc, Function.({
id = None;
params;
body;
generator;
async;
(* TODO: add support for method predicates *)
predicate = None;
expression;
returnType;
typeParameters;
}) in
let kind = Ast.(match static, key with
| false, Expression.Object.Property.Identifier (_, "constructor")
| false, 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;
static;
decorators;
})))
match abstract, key with
| Some loc, Ast.Expression.Object.Property.Identifier (_, "constructor")
| Some loc, Ast.Expression.Object.Property.Literal (_, {
Literal.value = Literal.String "constructor";
_;
}) ->
error_at env (loc, Error.UnexpectedAbstract);
init env start_loc decorators key async generator None static variance
| Some abstract_loc, _ ->
error_unsupported_variance env variance;
Ast.(match static, key with
| false, Expression.Object.Property.Identifier (_, "constructor")
| false, Expression.Object.Property.Literal (_, {
Literal.value = Literal.String "constructor";
_;
}) ->
error_at env (abstract_loc, Error.UnexpectedAbstract);
init env start_loc decorators key async generator None static variance
| _ ->
let value = abstract_method_value env in
let loc = Loc.btwn start_loc (fst value) in
Class.(Body.AbstractMethod (loc, AbstractMethod.({
key;
value;
static;
}))))
| None, _ ->
error_unsupported_variance env variance;
let value = method_value env async generator in
let kind = Ast.(match static, key with
| false, Expression.Object.Property.Identifier (_, "constructor")
| false, Expression.Object.Property.Literal (_, {
Literal.value = Literal.String "constructor";
_;
}) ->
Class.Method.Constructor
| _ ->
Class.Method.Method)
in
Ast.Class.(Body.Method (Loc.btwn start_loc (fst value), Method.({
key;
value;
kind;
static;
decorators;
})))

in fun env -> Ast.Expression.Object.Property.(
let start_loc = Peek.loc env in
let decorators = decorator_list env in
let abstract =
let loc = Peek.loc env in
if Peek.token ~i:1 env <> T_LPAREN
&& Peek.token ~i:1 env <> T_LESS_THAN
&& Expect.maybe env T_ABSTRACT
then Some loc else None
in
let static =
Peek.token ~i:1 env <> T_LPAREN &&
Peek.token ~i:1 env <> T_LESS_THAN &&
Expand All @@ -445,9 +502,10 @@ module Object
| T_ASSIGN
| T_SEMICOLON
| T_LPAREN ->
init env start_loc decorators key async generator static variance
init env start_loc decorators key async generator abstract static variance
| _ ->
error_unsupported_variance env variance;
error_unsupported_abstract env abstract;
get env start_loc decorators static)
| false, false,
(_, (Identifier (_, "set") as key)) ->
Expand All @@ -457,12 +515,13 @@ module Object
| T_ASSIGN
| T_SEMICOLON
| T_LPAREN ->
init env start_loc decorators key async generator static variance
init env start_loc decorators key async generator abstract static variance
| _ ->
error_unsupported_variance env variance;
error_unsupported_abstract env abstract;
set env start_loc decorators static)
| _, _, (_, key) ->
init env start_loc decorators key async generator static variance
init env start_loc decorators key async generator abstract static variance
)

let class_declaration env decorators =
Expand Down
2 changes: 2 additions & 0 deletions src/parser/parse_error.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type t =
| UnexpectedIdentifier
| UnexpectedReserved
| UnexpectedEOS
| UnexpectedAbstract
| UnexpectedVariance
| UnexpectedTypeAlias
| UnexpectedOpaqueTypeAlias
Expand Down Expand Up @@ -107,6 +108,7 @@ module PP =
| UnexpectedIdentifier -> "Unexpected identifier"
| UnexpectedReserved -> "Unexpected reserved word"
| UnexpectedEOS -> "Unexpected end of input"
| UnexpectedAbstract -> "Unexpected abstract"
| UnexpectedVariance -> "Unexpected variance sigil"
| UnexpectedTypeAlias -> "Type aliases are not allowed in untyped mode"
| UnexpectedOpaqueTypeAlias -> "Opaque type aliases are not allowed in untyped mode"
Expand Down
1 change: 1 addition & 0 deletions src/parser/parser_flow.ml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ module rec Parse : PARSER = struct
| T_CASE
| T_DEFAULT
| T_EXTENDS
| T_ABSTRACT
| T_STATIC
| T_EXPORT (* TODO *)
| T_ELLIPSIS ->
Expand Down
19 changes: 19 additions & 0 deletions src/parser/test/custom_ast_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,22 @@ def('Import')

def('CallExpression')
.field('callee', or(def('Expression'), def('Import')));

def("AbstractMethodDefinition")
.bases("Declaration")
.build("kind", "key", "value", "static")
.field("key", or(def("Literal"), def("Identifier"), def("Expression")))
.field("value", def("FunctionTypeAnnotation"))
.field("computed", Boolean, defaults["false"])
.field("static", Boolean, defaults["false"]);

var ClassBodyElement = or(
def("AbstractMethodDefinition"),
def("MethodDefinition"),
def("VariableDeclarator"),
def("ClassPropertyDefinition"),
def("ClassProperty")
);

def("ClassBody")
.field("body", [ClassBodyElement]);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class Foo { abstract myMethod(number): void; }
Loading