Skip to content

Commit

Permalink
[flow][parser] add support for const type parameter
Browse files Browse the repository at this point in the history
Summary:
Adds support for parsing a `const` modifier in type parameters:
```
function foo<const T>(x: T): void {}
```
Typing won't be supported for now; any instance of `const` type param is an unsupported-syntax error.

`const` won't be a valid name for a type parameter anymore, so the following will now be parsing errors:
```
function foo<const>(): void {};
type T<const> = {};
class C<const> {};
```

Changelog: [internal]

Reviewed By: SamChou19815

Differential Revision: D66669559

fbshipit-source-id: 17e33fb421f4e626c66c0db679b7816305c23bd5
  • Loading branch information
panagosg7 authored and facebook-github-bot committed Dec 4, 2024
1 parent 638aa1c commit f0cfd43
Show file tree
Hide file tree
Showing 31 changed files with 971 additions and 10 deletions.
5 changes: 4 additions & 1 deletion packages/flow-parser/test/custom_ast_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/scope_builder.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/common/ty/ty_serializer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand Down Expand Up @@ -491,6 +492,7 @@ let type_ options =
bound_kind = T.TypeParam.Colon;
variance = variance_ tp.tp_polarity;
default;
const = None;
}
)
and type_arguments ts =
Expand Down
4 changes: 4 additions & 0 deletions src/parser/estree_translator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,7 @@ with type t = Impl.t = struct
bound_kind;
variance = tp_var;
default;
const;
}
) =
node
Expand All @@ -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 -> []
Expand Down
5 changes: 5 additions & 0 deletions src/parser/flow_ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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' = {
Expand All @@ -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
Expand Down
30 changes: 27 additions & 3 deletions src/parser/flow_ast_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/parser/flow_lexer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/parser/parser_env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ let is_reserved_type str_val =
| "bigint"
| "bool"
| "boolean"
| "const"
| "empty"
| "extends"
| "false"
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let x: {[const key in keyof O]?: T};
193 changes: 193 additions & 0 deletions src/parser/test/flow/types/mapped_types/invalid_const_tparam.tree.json
Original file line number Diff line number Diff line change
@@ -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":[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
function f1<const X>(): void {}
function f2<const X, Y>(): void {}
function f3<X, const Y>(): void {}
function f4<const Y = T>(): void {}
function f5<const Y : T>(): void {}
function f6 < /* comment 1 */ const /* comment 2 */ Y /* comment 3 */ : /* comment 4 */ T /* comment 5 */ >(): void {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"intern_comments": true
}
Loading

0 comments on commit f0cfd43

Please sign in to comment.