diff --git a/packages/flow-parser/test/custom_ast_types.js b/packages/flow-parser/test/custom_ast_types.js index f44c286843c..09d1bb85530 100644 --- a/packages/flow-parser/test/custom_ast_types.js +++ b/packages/flow-parser/test/custom_ast_types.js @@ -359,11 +359,14 @@ function custom_ast_types(fork) { .bases('FlowType') .build('argument') .field('argument', def('FlowType')); - def('TypeParameter').field('usesExtendsBound', or(undefined, Boolean)); + def('TypeParameter') + .field('usesExtendsBound', or(undefined, Boolean)) + .field('const', or(def('ConstModifier'), null), defaults['null']); def('Variance').field( 'kind', or('plus', 'minus', 'readonly', 'in', 'out', 'in-out'), ); + def('ConstModifier').bases('Node'); def('AsExpression') .bases('Expression') .build('expression', 'typeAnnotation') diff --git a/src/analysis/scope_builder.ml b/src/analysis/scope_builder.ml index c83cafb858b..5706a3feb62 100644 --- a/src/analysis/scope_builder.ml +++ b/src/analysis/scope_builder.ml @@ -630,7 +630,7 @@ module Make (L : Loc_sig.S) (Api : Scope_api_sig.S with module L = L) : in let rec loop tps = match tps with - | (loc, { name; bound; bound_kind = _; variance; default }) :: next -> + | (loc, { name; bound; bound_kind = _; variance; default; const = _ }) :: next -> hoist_op (fun () -> ignore @@ this#type_annotation_hint bound); ignore @@ this#variance_opt variance; hoist_op (fun () -> ignore @@ Base.Option.map ~f:this#type_ default); diff --git a/src/common/ty/ty_serializer.ml b/src/common/ty/ty_serializer.ml index db3da150e1b..bddf72164c7 100644 --- a/src/common/ty/ty_serializer.ml +++ b/src/common/ty/ty_serializer.ml @@ -156,6 +156,7 @@ let type_ options = bound_kind = T.TypeParam.Extends; variance = None; default = None; + const = None; } in just (T.Infer { T.Infer.tparam; comments = None }) @@ -491,6 +492,7 @@ let type_ options = bound_kind = T.TypeParam.Colon; variance = variance_ tp.tp_polarity; default; + const = None; } ) and type_arguments ts = diff --git a/src/parser/estree_translator.ml b/src/parser/estree_translator.ml index cae07609c0a..688975a49d3 100644 --- a/src/parser/estree_translator.ml +++ b/src/parser/estree_translator.ml @@ -2187,6 +2187,7 @@ with type t = Impl.t = struct bound_kind; variance = tp_var; default; + const; } ) = node @@ -2201,6 +2202,9 @@ with type t = Impl.t = struct ("variance", option variance tp_var); ("default", option _type default); ] + @ (match const with + | Some (loc, comments) -> [("const", node ?comments "ConstModifier" loc [])] + | None -> []) @ match bound_kind with | Type.TypeParam.Colon -> [] diff --git a/src/parser/flow_ast.ml b/src/parser/flow_ast.ml index d1ca57393d0..0b02c69f547 100644 --- a/src/parser/flow_ast.ml +++ b/src/parser/flow_ast.ml @@ -618,6 +618,10 @@ and Type : sig | Extends [@@deriving show] + module ConstModifier : sig + type 'M t = 'M * ('M, unit) Syntax.t option [@@deriving show] + end + type ('M, 'T) t = 'M * ('M, 'T) t' and ('M, 'T) t' = { @@ -626,6 +630,7 @@ and Type : sig bound_kind: bound_kind; variance: 'M Variance.t option; default: ('M, 'T) Type.t option; + const: 'M ConstModifier.t option; } [@@deriving show] end diff --git a/src/parser/flow_ast_mapper.ml b/src/parser/flow_ast_mapper.ml index 06ecd817e84..173e0dbb21d 100644 --- a/src/parser/flow_ast_mapper.ml +++ b/src/parser/flow_ast_mapper.ml @@ -1547,6 +1547,14 @@ class ['loc] mapper = method variance_opt (opt : 'loc Ast.Variance.t option) = map_opt this#variance opt + method tparam_const_modifier (c : 'loc Ast.Type.TypeParam.ConstModifier.t) = + let (loc, comments) = c in + let comments' = this#syntax_opt comments in + if comments == comments' then + c + else + (loc, comments') + method type_args (targs : ('loc, 'loc) Ast.Type.TypeArgs.t) = let open Ast.Type.TypeArgs in let (loc, { arguments; comments }) = targs in @@ -1569,15 +1577,31 @@ class ['loc] mapper = method type_param (tparam : ('loc, 'loc) Ast.Type.TypeParam.t) = let open Ast.Type.TypeParam in - let (loc, { name; bound; bound_kind; variance; default }) = tparam in + let (loc, { name; bound; bound_kind; variance; default; const }) = tparam in let bound' = this#type_annotation_hint bound in let variance' = this#variance_opt variance in let default' = map_opt this#type_ default in + let const' = map_opt this#tparam_const_modifier const in let name' = this#binding_type_identifier name in - if name' == name && bound' == bound && variance' == variance && default' == default then + if + name' == name + && bound' == bound + && variance' == variance + && default' == default + && const' == const + then tparam else - (loc, { name = name'; bound = bound'; bound_kind; variance = variance'; default = default' }) + ( loc, + { + name = name'; + bound = bound'; + bound_kind; + variance = variance'; + default = default'; + const = const'; + } + ) method generic_type _loc (gt : ('loc, 'loc) Ast.Type.Generic.t) = let open Ast.Type.Generic in diff --git a/src/parser/flow_lexer.ml b/src/parser/flow_lexer.ml index 58f63ecd8f9..41d35e96073 100644 --- a/src/parser/flow_lexer.ml +++ b/src/parser/flow_lexer.ml @@ -1785,6 +1785,7 @@ let type_token env lexbuf = | "bigint" -> Token (env, T_BIGINT_TYPE) | "bool" -> Token (env, T_BOOLEAN_TYPE BOOL) | "boolean" -> Token (env, T_BOOLEAN_TYPE BOOLEAN) + | "const" -> Token (env, T_CONST) | "empty" -> Token (env, T_EMPTY_TYPE) | "extends" -> Token (env, T_EXTENDS) | "false" -> Token (env, T_FALSE) diff --git a/src/parser/parser_env.ml b/src/parser/parser_env.ml index 94025508795..bc40743638c 100644 --- a/src/parser/parser_env.ml +++ b/src/parser/parser_env.ml @@ -661,6 +661,7 @@ let is_reserved_type str_val = | "bigint" | "bool" | "boolean" + | "const" | "empty" | "extends" | "false" @@ -691,6 +692,7 @@ let token_is_reserved_type t = | T_ANY_TYPE | T_BIGINT_TYPE | T_BOOLEAN_TYPE _ + | T_CONST | T_EMPTY_TYPE | T_EXTENDS | T_FALSE diff --git a/src/parser/test/flow/types/mapped_types/invalid_const_tparam.js b/src/parser/test/flow/types/mapped_types/invalid_const_tparam.js new file mode 100644 index 00000000000..b09b2ac1372 --- /dev/null +++ b/src/parser/test/flow/types/mapped_types/invalid_const_tparam.js @@ -0,0 +1 @@ +let x: {[const key in keyof O]?: T}; diff --git a/src/parser/test/flow/types/mapped_types/invalid_const_tparam.tree.json b/src/parser/test/flow/types/mapped_types/invalid_const_tparam.tree.json new file mode 100644 index 00000000000..feeb32d362d --- /dev/null +++ b/src/parser/test/flow/types/mapped_types/invalid_const_tparam.tree.json @@ -0,0 +1,193 @@ +{ + "errors":[ + { + "loc":{"source":null,"start":{"line":1,"column":9},"end":{"line":1,"column":14}}, + "message":"Unexpected token `const`, expected a type" + }, + { + "loc":{"source":null,"start":{"line":1,"column":9},"end":{"line":1,"column":14}}, + "message":"Unexpected token `const`, expected the token `]`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":15},"end":{"line":1,"column":18}}, + "message":"Unexpected identifier, expected the token `:`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":27}}, + "message":"Unexpected token `keyof`, expected the token `,`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":29}}, + "message":"Unexpected identifier, expected the token `:`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":29}}, + "message":"Unexpected identifier, expected the token `,`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":30}}, + "message":"Unexpected token `]`, expected the token `:`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":30}}, + "message":"Unexpected token `]`, expected the token `,`" + }, + { + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":30}}, + "message":"Unexpected token `]`, expected an identifier" + } + ], + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "range":[0,36], + "body":[ + { + "type":"VariableDeclaration", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "range":[0,36], + "declarations":[ + { + "type":"VariableDeclarator", + "loc":{"source":null,"start":{"line":1,"column":4},"end":{"line":1,"column":35}}, + "range":[4,35], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":4},"end":{"line":1,"column":35}}, + "range":[4,35], + "name":"x", + "typeAnnotation":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":35}}, + "range":[5,35], + "typeAnnotation":{ + "type":"ObjectTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":7},"end":{"line":1,"column":35}}, + "range":[7,35], + "inexact":false, + "exact":false, + "properties":[ + { + "type":"ObjectTypeProperty", + "loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":27}}, + "range":[22,27], + "key":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":27}}, + "range":[22,27], + "name":"keyof", + "typeAnnotation":null, + "optional":false + }, + "value":{ + "type":"AnyTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":27}}, + "range":[22,27] + }, + "method":false, + "optional":false, + "static":false, + "proto":false, + "variance":null, + "kind":"init" + }, + { + "type":"ObjectTypeProperty", + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":29}}, + "range":[28,29], + "key":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":29}}, + "range":[28,29], + "name":"O", + "typeAnnotation":null, + "optional":false + }, + "value":{ + "type":"AnyTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":29}}, + "range":[28,29] + }, + "method":false, + "optional":false, + "static":false, + "proto":false, + "variance":null, + "kind":"init" + }, + { + "type":"ObjectTypeProperty", + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":34}}, + "range":[29,34], + "key":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":30}}, + "range":[29,30], + "name":"", + "typeAnnotation":null, + "optional":false + }, + "value":{ + "type":"GenericTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":33},"end":{"line":1,"column":34}}, + "range":[33,34], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":33},"end":{"line":1,"column":34}}, + "range":[33,34], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "method":false, + "optional":true, + "static":false, + "proto":false, + "variance":null, + "kind":"init" + } + ], + "indexers":[ + { + "type":"ObjectTypeIndexer", + "loc":{"source":null,"start":{"line":1,"column":8},"end":{"line":1,"column":21}}, + "range":[8,21], + "id":null, + "key":{ + "type":"AnyTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":9},"end":{"line":1,"column":14}}, + "range":[9,14] + }, + "value":{ + "type":"GenericTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":19},"end":{"line":1,"column":21}}, + "range":[19,21], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":19},"end":{"line":1,"column":21}}, + "range":[19,21], + "name":"in", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "static":false, + "variance":null + } + ], + "callProperties":[], + "internalSlots":[] + } + }, + "optional":false + }, + "init":null + } + ], + "kind":"let" + } + ], + "comments":[] +} diff --git a/src/parser/test/flow/types/type_params/function_const_params.js b/src/parser/test/flow/types/type_params/function_const_params.js new file mode 100644 index 00000000000..ece4b9a59a2 --- /dev/null +++ b/src/parser/test/flow/types/type_params/function_const_params.js @@ -0,0 +1,6 @@ +function f1(): void {} +function f2(): void {} +function f3(): void {} +function f4(): void {} +function f5(): void {} +function f6 < /* comment 1 */ const /* comment 2 */ Y /* comment 3 */ : /* comment 4 */ T /* comment 5 */ >(): void {} diff --git a/src/parser/test/flow/types/type_params/function_const_params.options.json b/src/parser/test/flow/types/type_params/function_const_params.options.json new file mode 100644 index 00000000000..0d0a7de49b1 --- /dev/null +++ b/src/parser/test/flow/types/type_params/function_const_params.options.json @@ -0,0 +1,3 @@ +{ + "intern_comments": true +} diff --git a/src/parser/test/flow/types/type_params/function_const_params.tree.json b/src/parser/test/flow/types/type_params/function_const_params.tree.json new file mode 100644 index 00000000000..0f1ed793f60 --- /dev/null +++ b/src/parser/test/flow/types/type_params/function_const_params.tree.json @@ -0,0 +1,476 @@ +{ + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":6,"column":118}}, + "range":[0,292], + "body":[ + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":31}}, + "range":[0,31], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":9},"end":{"line":1,"column":11}}, + "range":[9,11], + "name":"f1", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":1,"column":29},"end":{"line":1,"column":31}}, + "range":[29,31], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":28}}, + "range":[22,28], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":24},"end":{"line":1,"column":28}}, + "range":[24,28] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":1,"column":11},"end":{"line":1,"column":20}}, + "range":[11,20], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":1,"column":12},"end":{"line":1,"column":19}}, + "range":[12,19], + "name":"X", + "bound":null, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":1,"column":12},"end":{"line":1,"column":17}}, + "range":[12,17] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + }, + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":2,"column":0},"end":{"line":2,"column":34}}, + "range":[32,66], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":2,"column":9},"end":{"line":2,"column":11}}, + "range":[41,43], + "name":"f2", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":2,"column":32},"end":{"line":2,"column":34}}, + "range":[64,66], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":2,"column":25},"end":{"line":2,"column":31}}, + "range":[57,63], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":2,"column":27},"end":{"line":2,"column":31}}, + "range":[59,63] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":2,"column":11},"end":{"line":2,"column":23}}, + "range":[43,55], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":2,"column":12},"end":{"line":2,"column":19}}, + "range":[44,51], + "name":"X", + "bound":null, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":2,"column":12},"end":{"line":2,"column":17}}, + "range":[44,49] + } + }, + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":2,"column":21},"end":{"line":2,"column":22}}, + "range":[53,54], + "name":"Y", + "bound":null, + "variance":null, + "default":null + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + }, + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":3,"column":0},"end":{"line":3,"column":34}}, + "range":[67,101], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":3,"column":9},"end":{"line":3,"column":11}}, + "range":[76,78], + "name":"f3", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":3,"column":32},"end":{"line":3,"column":34}}, + "range":[99,101], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":3,"column":25},"end":{"line":3,"column":31}}, + "range":[92,98], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":3,"column":27},"end":{"line":3,"column":31}}, + "range":[94,98] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":3,"column":11},"end":{"line":3,"column":23}}, + "range":[78,90], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":3,"column":12},"end":{"line":3,"column":13}}, + "range":[79,80], + "name":"X", + "bound":null, + "variance":null, + "default":null + }, + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":3,"column":15},"end":{"line":3,"column":22}}, + "range":[82,89], + "name":"Y", + "bound":null, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":3,"column":15},"end":{"line":3,"column":20}}, + "range":[82,87] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + }, + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":4,"column":0},"end":{"line":4,"column":35}}, + "range":[102,137], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":4,"column":9},"end":{"line":4,"column":11}}, + "range":[111,113], + "name":"f4", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":4,"column":33},"end":{"line":4,"column":35}}, + "range":[135,137], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":4,"column":26},"end":{"line":4,"column":32}}, + "range":[128,134], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":4,"column":28},"end":{"line":4,"column":32}}, + "range":[130,134] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":4,"column":11},"end":{"line":4,"column":24}}, + "range":[113,126], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":4,"column":12},"end":{"line":4,"column":23}}, + "range":[114,125], + "name":"Y", + "bound":null, + "variance":null, + "default":{ + "type":"GenericTypeAnnotation", + "loc":{"source":null,"start":{"line":4,"column":22},"end":{"line":4,"column":23}}, + "range":[124,125], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":4,"column":22},"end":{"line":4,"column":23}}, + "range":[124,125], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":4,"column":12},"end":{"line":4,"column":17}}, + "range":[114,119] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + }, + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":5,"column":0},"end":{"line":5,"column":35}}, + "range":[138,173], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":9},"end":{"line":5,"column":11}}, + "range":[147,149], + "name":"f5", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":5,"column":33},"end":{"line":5,"column":35}}, + "range":[171,173], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":5,"column":26},"end":{"line":5,"column":32}}, + "range":[164,170], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":5,"column":28},"end":{"line":5,"column":32}}, + "range":[166,170] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":5,"column":11},"end":{"line":5,"column":24}}, + "range":[149,162], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":5,"column":12},"end":{"line":5,"column":23}}, + "range":[150,161], + "name":"Y", + "bound":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":5,"column":20},"end":{"line":5,"column":23}}, + "range":[158,161], + "typeAnnotation":{ + "type":"GenericTypeAnnotation", + "loc":{"source":null,"start":{"line":5,"column":22},"end":{"line":5,"column":23}}, + "range":[160,161], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":5,"column":22},"end":{"line":5,"column":23}}, + "range":[160,161], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + } + }, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":5,"column":12},"end":{"line":5,"column":17}}, + "range":[150,155] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + }, + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":6,"column":0},"end":{"line":6,"column":118}}, + "range":[174,292], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":6,"column":9},"end":{"line":6,"column":11}}, + "range":[183,185], + "name":"f6", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":6,"column":116},"end":{"line":6,"column":118}}, + "range":[290,292], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":6,"column":109},"end":{"line":6,"column":115}}, + "range":[283,289], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":6,"column":111},"end":{"line":6,"column":115}}, + "range":[285,289] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":6,"column":12},"end":{"line":6,"column":107}}, + "range":[186,281], + "params":[ + { + "type":"TypeParameter", + "trailingComments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":54},"end":{"line":6,"column":69}}, + "range":[228,243], + "value":" comment 3 " + } + ], + "leadingComments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":36},"end":{"line":6,"column":51}}, + "range":[210,225], + "value":" comment 2 " + } + ], + "loc":{"source":null,"start":{"line":6,"column":30},"end":{"line":6,"column":89}}, + "range":[204,263], + "name":"Y", + "bound":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":6,"column":70},"end":{"line":6,"column":89}}, + "range":[244,263], + "typeAnnotation":{ + "type":"GenericTypeAnnotation", + "loc":{"source":null,"start":{"line":6,"column":88},"end":{"line":6,"column":89}}, + "range":[262,263], + "id":{ + "type":"Identifier", + "trailingComments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":90},"end":{"line":6,"column":105}}, + "range":[264,279], + "value":" comment 5 " + } + ], + "leadingComments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":72},"end":{"line":6,"column":87}}, + "range":[246,261], + "value":" comment 4 " + } + ], + "loc":{"source":null,"start":{"line":6,"column":88},"end":{"line":6,"column":89}}, + "range":[262,263], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + } + }, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "leadingComments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":14},"end":{"line":6,"column":29}}, + "range":[188,203], + "value":" comment 1 " + } + ], + "loc":{"source":null,"start":{"line":6,"column":30},"end":{"line":6,"column":35}}, + "range":[204,209] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + } + ], + "comments":[ + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":14},"end":{"line":6,"column":29}}, + "range":[188,203], + "value":" comment 1 " + }, + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":36},"end":{"line":6,"column":51}}, + "range":[210,225], + "value":" comment 2 " + }, + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":54},"end":{"line":6,"column":69}}, + "range":[228,243], + "value":" comment 3 " + }, + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":72},"end":{"line":6,"column":87}}, + "range":[246,261], + "value":" comment 4 " + }, + { + "type":"Block", + "loc":{"source":null,"start":{"line":6,"column":90},"end":{"line":6,"column":105}}, + "range":[264,279], + "value":" comment 5 " + } + ] +} diff --git a/src/parser/test/flow/types/type_params/invalid_const_param_no_name.js b/src/parser/test/flow/types/type_params/invalid_const_param_no_name.js new file mode 100644 index 00000000000..402190cd377 --- /dev/null +++ b/src/parser/test/flow/types/type_params/invalid_const_param_no_name.js @@ -0,0 +1 @@ +function foo(): void {} diff --git a/src/parser/test/flow/types/type_params/invalid_const_param_no_name.tree.json b/src/parser/test/flow/types/type_params/invalid_const_param_no_name.tree.json new file mode 100644 index 00000000000..ddff9f85f78 --- /dev/null +++ b/src/parser/test/flow/types/type_params/invalid_const_param_no_name.tree.json @@ -0,0 +1,73 @@ +{ + "errors":[ + { + "loc":{"source":null,"start":{"line":1,"column":18},"end":{"line":1,"column":19}}, + "message":"Unexpected token `>`, expected an identifier" + }, + { + "loc":{"source":null,"start":{"line":1,"column":19},"end":{"line":1,"column":20}}, + "message":"Unexpected token `(`, expected the token `>`" + } + ], + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":30}}, + "range":[0,30], + "body":[ + { + "type":"FunctionDeclaration", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":30}}, + "range":[0,30], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":9},"end":{"line":1,"column":12}}, + "range":[9,12], + "name":"foo", + "typeAnnotation":null, + "optional":false + }, + "params":[], + "body":{ + "type":"BlockStatement", + "loc":{"source":null,"start":{"line":1,"column":28},"end":{"line":1,"column":30}}, + "range":[28,30], + "body":[] + }, + "returnType":{ + "type":"TypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":21},"end":{"line":1,"column":27}}, + "range":[21,27], + "typeAnnotation":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":23},"end":{"line":1,"column":27}}, + "range":[23,27] + } + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":1,"column":12},"end":{"line":1,"column":19}}, + "range":[12,19], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":1,"column":13},"end":{"line":1,"column":19}}, + "range":[13,19], + "name":"", + "bound":null, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":1,"column":13},"end":{"line":1,"column":18}}, + "range":[13,18] + } + } + ] + }, + "async":false, + "generator":false, + "predicate":null, + "expression":false + } + ], + "comments":[] +} diff --git a/src/parser/test/flow/types/type_params/type_const_params.js b/src/parser/test/flow/types/type_params/type_const_params.js new file mode 100644 index 00000000000..8560375122d --- /dev/null +++ b/src/parser/test/flow/types/type_params/type_const_params.js @@ -0,0 +1 @@ +type T = void; diff --git a/src/parser/test/flow/types/type_params/type_const_params.tree.json b/src/parser/test/flow/types/type_params/type_const_params.tree.json new file mode 100644 index 00000000000..ae4235890bf --- /dev/null +++ b/src/parser/test/flow/types/type_params/type_const_params.tree.json @@ -0,0 +1,47 @@ +{ + "type":"Program", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "range":[0,23], + "body":[ + { + "type":"TypeAlias", + "loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "range":[0,23], + "id":{ + "type":"Identifier", + "loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":6}}, + "range":[5,6], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{"source":null,"start":{"line":1,"column":6},"end":{"line":1,"column":15}}, + "range":[6,15], + "params":[ + { + "type":"TypeParameter", + "loc":{"source":null,"start":{"line":1,"column":7},"end":{"line":1,"column":14}}, + "range":[7,14], + "name":"X", + "bound":null, + "variance":null, + "default":null, + "const":{ + "type":"ConstModifier", + "loc":{"source":null,"start":{"line":1,"column":7},"end":{"line":1,"column":12}}, + "range":[7,12] + } + } + ] + }, + "right":{ + "type":"VoidTypeAnnotation", + "loc":{"source":null,"start":{"line":1,"column":18},"end":{"line":1,"column":22}}, + "range":[18,22] + } + } + ], + "comments":[] +} diff --git a/src/parser/type_parser.ml b/src/parser/type_parser.ml index 8178954acf9..eb646eda22e 100644 --- a/src/parser/type_parser.ml +++ b/src/parser/type_parser.ml @@ -68,6 +68,19 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ) | _ -> None + let maybe_const env = + match Peek.token env with + | T_CONST -> + Some + (with_loc + (fun env -> + let leading = Peek.comments env in + Eat.token env; + Flow_ast_utils.mk_comments_opt ~leading ()) + env + ) + | _ -> None + let number_singleton ~neg kind value raw env = if kind = LEGACY_OCTAL then strict_error env Parse_error.StrictOctalLiteral; let leading = Peek.comments env in @@ -529,6 +542,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct bound_kind = Type.TypeParam.Extends; variance = None; default = None; + const = None; }) env in @@ -1295,6 +1309,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct variance = None; default = None; bound_kind = Type.TypeParam.Colon; + const = None; } in (* We already checked in mapped_type_or_indexer that the next token was an @@ -1797,6 +1812,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let (param, require_default) = with_loc_extra (fun env -> + let const = maybe_const env in let variance = maybe_variance ~parse_in_out:true env in let (loc, (name, bound, bound_kind)) = bounded_type env in let (default, require_default) = @@ -1808,7 +1824,9 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct if require_default then error_at env (loc, Parse_error.MissingTypeParamDefault); (None, require_default) in - ({ Type.TypeParam.name; bound; bound_kind; variance; default }, require_default)) + ( { Type.TypeParam.name; bound; bound_kind; variance; default; const }, + require_default + )) env in (param :: acc, require_default) diff --git a/src/parser_utils/ast_builder.ml b/src/parser_utils/ast_builder.ml index b13fddd0579..59c925312c6 100644 --- a/src/parser_utils/ast_builder.ml +++ b/src/parser_utils/ast_builder.ml @@ -80,6 +80,7 @@ module Types = struct ?(bound_kind = Ast.Type.TypeParam.Colon) ?variance ?default + ?const name = ( loc, { @@ -88,6 +89,7 @@ module Types = struct bound_kind; variance; default; + const; } ) diff --git a/src/parser_utils/flow_ast_differ.ml b/src/parser_utils/flow_ast_differ.ml index 7125f3f8755..a59102b7dcc 100644 --- a/src/parser_utils/flow_ast_differ.ml +++ b/src/parser_utils/flow_ast_differ.ml @@ -3364,6 +3364,7 @@ let program (program1 : (Loc.t, Loc.t) Ast.Program.t) (program2 : (Loc.t, Loc.t) bound_kind = bound_kind1; variance = variance1; default = default1; + const = const1; } = t_param1 in @@ -3373,6 +3374,7 @@ let program (program1 : (Loc.t, Loc.t) Ast.Program.t) (program2 : (Loc.t, Loc.t) bound_kind = bound_kind2; variance = variance2; default = default2; + const = const2; } = t_param2 in @@ -3386,8 +3388,15 @@ let program (program1 : (Loc.t, Loc.t) Ast.Program.t) (program2 : (Loc.t, Loc.t) None in let default_diff = diff_if_changed_nonopt_fn type_ default1 default2 in + let const_diff = + if const1 = const2 then + Some [] + else + None + in let result = - join_diff_list [variance_diff; name_diff; bound_diff; bound_kind_diff; default_diff] + join_diff_list + [variance_diff; name_diff; bound_diff; bound_kind_diff; default_diff; const_diff] in Base.Option.value result diff --git a/src/parser_utils/flow_polymorphic_ast_mapper.ml b/src/parser_utils/flow_polymorphic_ast_mapper.ml index 65dd204bd22..5c18dfd6ef8 100644 --- a/src/parser_utils/flow_polymorphic_ast_mapper.ml +++ b/src/parser_utils/flow_polymorphic_ast_mapper.ml @@ -1236,13 +1236,21 @@ class virtual ['M, 'T, 'N, 'U] mapper = method type_param (tparam : ('M, 'T) Ast.Type.TypeParam.t) : ('N, 'U) Ast.Type.TypeParam.t = let open Ast.Type.TypeParam in - let (annot, { name; bound; bound_kind; variance; default }) = tparam in + let (annot, { name; bound; bound_kind; variance; default; const }) = tparam in let name' = this#type_param_identifier name in let bound' = this#type_annotation_hint bound in let variance' = this#variance_opt variance in let default' = Option.map ~f:this#type_ default in + let const' = Option.map ~f:this#tparam_const_modifier const in ( this#on_loc_annot annot, - { name = name'; bound = bound'; bound_kind; variance = variance'; default = default' } + { + name = name'; + bound = bound'; + bound_kind; + variance = variance'; + default = default'; + const = const'; + } ) method type_param_identifier (id : ('M, 'M) Ast.Identifier.t) : ('N, 'N) Ast.Identifier.t = @@ -2738,6 +2746,13 @@ class virtual ['M, 'T, 'N, 'U] mapper = method variance_opt (opt : 'M Ast.Variance.t option) : 'N Ast.Variance.t option = Option.map ~f:this#variance opt + method tparam_const_modifier (c : 'M Ast.Type.TypeParam.ConstModifier.t) + : 'N Ast.Type.TypeParam.ConstModifier.t = + let (loc, comments) = c in + let loc' = this#on_loc_annot loc in + let comments' = this#syntax_opt comments in + (loc', comments') + method while_ (stuff : ('M, 'T) Ast.Statement.While.t) : ('N, 'U) Ast.Statement.While.t = let open Ast.Statement.While in let { test; body; comments } = stuff in diff --git a/src/parser_utils/flow_polymorphic_ast_mapper.mli b/src/parser_utils/flow_polymorphic_ast_mapper.mli index 962d364fd69..ccd07972f9c 100644 --- a/src/parser_utils/flow_polymorphic_ast_mapper.mli +++ b/src/parser_utils/flow_polymorphic_ast_mapper.mli @@ -98,6 +98,9 @@ class virtual ['M, 'T, 'N, 'U] mapper : method component_body : 'M * ('M, 'T) Ast.Statement.Block.t -> 'N * ('N, 'U) Ast.Statement.Block.t + method tparam_const_modifier : + 'M Ast.Type.TypeParam.ConstModifier.t -> 'N Ast.Type.TypeParam.ConstModifier.t + method class_ : ('M, 'T) Ast.Class.t -> ('N, 'U) Ast.Class.t method class_body : ('M, 'T) Flow_ast.Class.Body.t -> ('N, 'U) Ast.Class.Body.t diff --git a/src/parser_utils/output/js_layout_generator.ml b/src/parser_utils/output/js_layout_generator.ml index 74c170ebbd8..f927f30bb29 100644 --- a/src/parser_utils/output/js_layout_generator.ml +++ b/src/parser_utils/output/js_layout_generator.ml @@ -3314,6 +3314,12 @@ and variance (loc, { Ast.Variance.kind; comments }) = | Ast.Variance.InOut -> fuse [Atom "in out"; space] ) +and const const_ = + if Base.Option.is_some const_ then + fuse [Atom "const"; space] + else + Empty + and match_case_guard ~opts guard = Base.Option.value_map guard ~default:Empty ~f:(fun e -> fuse [space; Atom "if"; space; expression ~opts e] @@ -3492,10 +3498,12 @@ and type_param bound_kind; variance = variance_; default; + const = const_; } ) = fuse [ + const const_; option variance variance_; source_location_with_comments ?comments (loc, Atom name); hint diff --git a/src/parser_utils/type_sig/type_sig_parse.ml b/src/parser_utils/type_sig/type_sig_parse.ml index 0cb81d81c69..1d651c99434 100644 --- a/src/parser_utils/type_sig/type_sig_parse.ml +++ b/src/parser_utils/type_sig/type_sig_parse.ml @@ -1891,6 +1891,7 @@ and object_type = bound_kind = _; variance = _; default = _; + const = _; } ) = key_tparam @@ -2515,6 +2516,7 @@ and tparam opts scope tbls xs tp = bound_kind = _; variance = v; default = d; + const = _; } ) = tp diff --git a/src/typing/errors/flow_intermediate_error.ml b/src/typing/errors/flow_intermediate_error.ml index 0c87b29acaa..460216913c4 100644 --- a/src/typing/errors/flow_intermediate_error.ml +++ b/src/typing/errors/flow_intermediate_error.ml @@ -3899,6 +3899,8 @@ let to_printable_error : [text "Unsupported static internal slot "; code name; text "."] | MessageUnsupportedSyntax WithStatement -> [text "Flow doesn't support "; code "with"; text " statements."] + | MessageUnsupportedSyntax ConstTypeParameter -> + [text "Const type parameters are not yet supported. This modifier will be ignored."] | MessageUnsupportedVarianceAnnotation kind -> [text "Variance modifiers cannot appear on a type parameter of a "; text kind; text "."] | MessageUntypedImport module_name -> diff --git a/src/typing/errors/flow_intermediate_error_types.ml b/src/typing/errors/flow_intermediate_error_types.ml index 556ead3c155..19197fcaf64 100644 --- a/src/typing/errors/flow_intermediate_error_types.ml +++ b/src/typing/errors/flow_intermediate_error_types.ml @@ -87,6 +87,7 @@ type unsupported_syntax = | WithStatement | ComponentSyntax | DeclareNamespace + | ConstTypeParameter type 'loc invalid_render_type_kind = | InvalidRendersNullVoidFalse diff --git a/src/typing/type_annotation.ml b/src/typing/type_annotation.ml index 7c34e098c7f..3afc632f6ea 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -2576,11 +2576,18 @@ module Make (Statement : Statement_sig.S) : Type_annotation_sig.S = struct bound_kind; variance; default; + const; } = type_param in let reason = mk_annot_reason (RType (OrdinaryName name)) name_loc in let polarity = polarity cx variance in + Base.Option.iter const ~f:(fun (loc, _) -> + Flow_js_utils.add_output + env.cx + (Error_message.EUnsupportedSyntax (loc, Flow_intermediate_error_types.ConstTypeParameter) + ) + ); (match bound_kind with | Ast.Type.TypeParam.Extends when not (from_infer_type || Context.ts_syntax cx) -> Flow_js_utils.add_output @@ -2648,6 +2655,7 @@ module Make (Statement : Statement_sig.S) : Type_annotation_sig.S = struct bound_kind; variance; default = default_ast; + const; } ) in diff --git a/src/typing/typed_ast_finder.ml b/src/typing/typed_ast_finder.ml index 85ed52c6df3..bb4b108d36e 100644 --- a/src/typing/typed_ast_finder.ml +++ b/src/typing/typed_ast_finder.ml @@ -165,7 +165,7 @@ class type_parameter_mapper = method private make_typeparam tparam = let open Ast.Type.TypeParam in - let (_, { name = id; bound; bound_kind = _; variance; default }) = tparam in + let (_, { name = id; bound; bound_kind = _; variance; default; const = _ }) = tparam in let (name_loc, { Ast.Identifier.name; comments = _ }) = id in let reason = Reason.mk_annot_reason (Reason.RType (Reason.OrdinaryName name)) name_loc in let bound = diff --git a/tests/const_type_param_unsupported/.flowconfig b/tests/const_type_param_unsupported/.flowconfig new file mode 100644 index 00000000000..08bb313bf3e --- /dev/null +++ b/tests/const_type_param_unsupported/.flowconfig @@ -0,0 +1,12 @@ +[ignore] + +[include] + +[libs] + +[lints] + +[options] +all=true + +[strict] diff --git a/tests/const_type_param_unsupported/const_type_param_unsupported.exp b/tests/const_type_param_unsupported/const_type_param_unsupported.exp new file mode 100644 index 00000000000..ec51354b809 --- /dev/null +++ b/tests/const_type_param_unsupported/const_type_param_unsupported.exp @@ -0,0 +1,34 @@ +Error ----------------------------------------------------------------------------------------------------- file.js:1:13 + +Const type parameters are not yet supported. This modifier will be ignored. [unsupported-syntax] + + 1| function f1(): void {} + ^^^^^ + + +Error ------------------------------------------------------------------------------------------------------ file.js:2:9 + +Const type parameters are not yet supported. This modifier will be ignored. [unsupported-syntax] + + 2| class C { + ^^^^^ + + +Error ------------------------------------------------------------------------------------------------------ file.js:3:7 + +Const type parameters are not yet supported. This modifier will be ignored. [unsupported-syntax] + + 3| m(x: X, t: T) {} + ^^^^^ + + +Error ------------------------------------------------------------------------------------------------------ file.js:5:8 + +Const type parameters are not yet supported. This modifier will be ignored. [unsupported-syntax] + + 5| type T = void; + ^^^^^ + + + +Found 4 errors diff --git a/tests/const_type_param_unsupported/file.js b/tests/const_type_param_unsupported/file.js new file mode 100644 index 00000000000..8b5f55fd496 --- /dev/null +++ b/tests/const_type_param_unsupported/file.js @@ -0,0 +1,5 @@ +function f1(): void {} +class C { + m(x: X, t: T) {} +} +type T = void;